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中的数据对应关系。

 

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