我试图为具有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 ¶m) {
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 ¶m) {
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
我不确定问题是什么。
答案 0 :(得分:2)
Synch
方法不太安全:它依赖于taskcount
,但taskcount
在Do
中递增。例如。如果主线程在SupplyTask
的至少一个线程获得任务(并且增加ThreadPool
)之前通过taskcount
生成所有任务,则Synch
将等待nobody和主线程将开始新的阶段。尝试将taskcount++
从Do
移至SupplyTask
。