Parallel Quicksort using a stack of pending chunks to sort

代碼源自C++ Concurrency in Action Second Edition

#include <list>

template<typename T>
struct sorter
{
    struct chunk_to_sort
    {
        std::list<T> data;
        std::promise<std::list<T> > promise;
    };
    thread_safe_stack<chunk_to_sort> chunks;
    std::vector<std::thread> threads;
    unsigned const max_thread_count;
    std::atomic<bool> end_of_data;
    sorter() :
        max_thread_count(std::thread::hardware_concurrency() - 1),
        end_of_data(false)
    {}
    ~sorter()
    {
        end_of_data = true;
        for (unsigned i = 0; i < threads.size(); ++i)
        {
            threads[i].join();
        }
    }
    void try_sort_chunk()
    {
        std::shared_ptr<chunk_to_sort > chunk = chunks.pop();
        if (chunk)
        {
            sort_chunk(chunk);
        }
    }
    std::list<T> do_sort(std::list<T>& chunk_data)
    {
        if (chunk_data.empty())
        {
            return chunk_data;
        }
        std::list<T> result;
        // 將chunk_data中第一個元素取出放入result
        result.splice(result.begin(), chunk_data, chunk_data.begin());
        // 將該元素值作爲base值
        T const& partition_val = *result.begin();
        // 將chunk_data中剩餘元素根據base值分區
        typename std::list<T>::iterator divide_point =
            std::partition(chunk_data.begin(), chunk_data.end(),
                [&](T const& val) {return val < partition_val; });
        // 小於base值的部分保存至new_lower_chunk,splice之後的chunk_data即爲大於base值的部分
        chunk_to_sort new_lower_chunk;
        new_lower_chunk.data.splice(new_lower_chunk.data.end(),
            chunk_data, chunk_data.begin(),
            divide_point);
        // 小於base部分的list壓入全局類型棧
        std::future<std::list<T> > new_lower =
            new_lower_chunk.promise.get_future();
        chunks.push(std::move(new_lower_chunk));
        // 線程數組添加線程 ??如果超過最大線程數會怎樣??
        if (threads.size() < max_thread_count)
        {
            threads.push_back(std::thread(&sorter<T>::sort_thread, this));
        }
        // 大於base值的部分遞歸調用自身,繼續進行分區排序
        std::list<T> new_higher(do_sort(chunk_data));
        // 將排好序的大於base部分的list拼接到result
        result.splice(result.end(), new_higher);
        // 小於base部分的list如果未完成則調用try_sort_chunk
        while (new_lower.wait_for(std::chrono::seconds(0)) !=
            std::future_status::ready)
        {
            try_sort_chunk();
        }
        // 將排好序的小於base部分list拼接到result,形成完整排好序的list
        result.splice(result.begin(), new_lower.get());
        // 返回結果
        return result;
    }
    void sort_chunk(std::shared_ptr<chunk_to_sort > const& chunk)
    {
        chunk->promise.set_value(do_sort(chunk->data));
    }
    void sort_thread()
    {
        while (!end_of_data)
        {
            try_sort_chunk();
            std::this_thread::yield();
        }
    }
};

template<typename T>
std::list<T> parallel_quick_sort(std::list<T> input)
{
    if (input.empty())
    {
        return input;
    }
    sorter<T> s;
    return s.do_sort(input);
}

do_sort函數中每次將list切割爲大小兩部分後,大的部分遞歸調用do_sort,小的部分起一個線程,並將list入棧,每個線程pop一個list來進行處理 ,棧的下一層用future等待上一層完成,每個線程都一直嘗試pop stack中的list進行sort,直至全部排序完成。

這裏只是儘可能多的起線程去處理stack中的list,不是和stack中的數據對應關係。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章