C ++:用于较低较低并行前缀计算的线程池

时间:2014-05-23 12:21:31

标签: c++ multithreading parallel-processing

我试图为具有2 ^ N个元素的任何向量实现上/下并行前缀。请注意,我从一些书中使用的算法为这种有限的向量长度提供了代码。无论如何,我试图实现一个天真的线程池,这是我第一次编写多线程应用程序。我使用的算法根据向量的长度有多个前缀计算级别,并且特定级别的每个操作都可以并行执行。

for(int k = 0; k < std::log2(vector.size()); k++) {
    for(int r = 0; r < n / 2; r++) {
        AddInParallel(vector, r, k);
    }
}

这里的并行工作只是将两个参数(r,k)传递给线程。所以我编写了一个使用双端队列的ThreadPool类。参数从一端推送到deque,另一端从线程读取。然后,调用AddInParallel。但是,似乎有一些竞争条件(不确定它是否是合适的术语)我的结果不正确。

我试图保护调用AddInParallel但未发生任何变化的范围

这是ThreadPool类

typedef std::lock_guard<std::mutex> Guard;
class ThreadPool {
public:
static ThreadPool &Instance() {
    static ThreadPool instance;
    return instance;
}

void SupplyTask(const TaskParameter &param) {
    Guard guard(Mutex());
    tasks.emplace_front(param);
}

void Finalize() {
    done = true;
    for(std::size_t i = 0; i < threads.size(); i++) {
        threads[i].join();
    }
}

void Synch() {
    while(taskcount) {
        ;
    }
}

private:
ThreadPool(): done(false), taskcount(0), threads(CONCURRENCY) {
    for(std::size_t i = 0; i < threads.size(); i++) {
        threads[i] = std::thread(&ThreadPool::Do, this, i);
    }
}

ThreadPool(const ThreadPool &pool) = delete;
ThreadPool &operator=(const ThreadPool &pool) = delete;

static std::mutex &Mutex() {
    static std::mutex mutex;
    return mutex;
}

bool PollTask(TaskParameter &param) {
    Guard guard(Mutex());
    if(!tasks.empty()) {
        param = tasks.back();
        tasks.pop_back();
        return true;
    }
    return false;
}

void Print(const unsigned int id, const unsigned int r, const unsigned int k) {
    Guard guard(Mutex());
    std::cout << "Thread ID: " << id << std::endl;
    std::cout << "r: " << r << std::endl << "k: " << k << std::endl;
    std::cout << "------------" << std::endl;
}

void Do(unsigned int id) {
    TaskParameter param;
    bool havetask;
    while(!done) {
        bool havetask = PollTask(param);
        if(havetask) {
            taskcount++;
            Print(id, param.r, param.k);
            AddInParallel(*param.vector, param.r, param.k);
            taskcount--;
        }
        else {
            std::this_thread::yield();
        }
    }            
}

std::atomic_bool done;
std::atomic_uint taskcount;
std::vector<std::thread> threads;
std::deque<TaskParameter> tasks;

static const std::size_t CONCURRENCY;
};
const std::size_t ThreadPool::CONCURRENCY = 7;

因为,每个级别都依赖于前一个级别的计算,我尝试在进入下一级别之前同步所有线程,如下所示

for (k = 0; k < logn; k++) {
    for (r = 0; r < n / 2; r++) {
        ThreadPool::Instance().SupplyTask(TaskParameter(vector, r, k));
    }
    ThreadPool::Instance().Synch();
}

样本运行长度为2 ^ 4:

输入:1 ... 16

输出:1,3,6,10,15,21,28,46,55,65,76,88,101,115,130,146

预期:1,3,6,10,15,21,28,36,45,55,66,78,91,105,120,136

我不确定问题是什么。

1 个答案:

答案 0 :(得分:2)

Synch方法不太安全:它依赖于taskcount,但taskcountDo中递增。例如。如果主线程在SupplyTask的至少一个线程获得任务(并且增加ThreadPool)之前通过taskcount生成所有任务,则Synch将等待nobody和主线程将开始新的阶段。尝试将taskcount++Do移至SupplyTask