Pthreads - 将顺序程序转换为并行程序



我正在用C++模拟"康威的生命游戏",其中 2d 矩阵表示板,0 是空单元格,而 1 是活单元格。我最初是按顺序写的,并试图使其与 pthreads 并行。但由于某种原因,该程序不再按预期运行。虽然它经历了两个循环并且似乎拾取了一些"count++",但它并没有拾取所有"count++",因此每一轮单元格都被评估为只有一个或零个邻居(即使情况并非如此)。这导致设定时间段后的"结果"全部为零,因为每个细胞都死亡而无法繁殖。我已经为此工作了几天并改变了不同的东西,但仍然无法弄清楚。这是我的代码:

#include <iostream>
#include <vector>
#include <pthread.h>
#include <cstdlib>
#include <functional>
using namespace std;
pthread_mutex_t mymutex;
int lifetime, numthreads = 5;
vector<vector<int> > board,result,pending;
void *loader(void *tid){
long thid = long(tid);
int n = board.size();
result = board;
int count = 0;
for(long i = 0; i < n; i ++){
if(i % numthreads != thid)
continue;
for(long j = 0; j < n ; j++){
if(i % numthreads != thid)
continue;
if(i+1 < n){
if(result[i+1][j] == 1) //checking each of the neighbor
count++
;
if(j+1 < n){
if(result[i+1][j+1] == 1)
count++;
}
if(j-1 >= 0){
if(result[i+1][j-1] == 1)
count++;
}
}
if(j-1 >= 0){
if(result[i][j-1] == 1)
count++;
}
if(j+1 < n){
if(result[i][j+1] == 1)
count++;
}
if(i-1 >= 0){
if(result[i-1][j] == 1)
count++;
if(j+1 < n){
if(result[i-1][j+1] == 1)
count++;
}
if(j-1 >= 0){
if(result[i-1][j-1] == 1)
count++;
}
}
//determining next state
if(count <= 1 || count >= 4){ //this utilizes the three main rules of game
pthread_mutex_lock(&mymutex);
pending[i][j] = 0;
pthread_mutex_unlock(&mymutex);
}else if(count == 3){
pthread_mutex_lock(&mymutex);
pending[i][j] = 1;
pthread_mutex_unlock(&mymutex);
}else{
pthread_mutex_lock(&mymutex);
pending[i][j] = result[i][j];
pthread_mutex_unlock(&mymutex);
}
count = 0;
pthread_mutex_lock(&mymutex);
result = pending;
pthread_mutex_unlock(&mymutex);
}
}
pthread_exit(NULL);
return NULL;
}
int main(){
//setting up input
int n;
cin >> n;
board.resize(n);
result.resize(n);
pending.resize(n);
for(int i = 0; i < board.size(); i++){
board[i].resize(n);
result[i].resize(n);
pending[i].resize(n);
}
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cin >> board[i][j];
}
}
cin >> lifetime;
//making threads, enacting fn
pthread_t threads[numthreads];
void *status[numthreads];
pthread_mutex_init(&mymutex,NULL);
int rc;
for(int i = 0; i < lifetime; i++){
for(int t = 0; t < numthreads; t++){
rc = pthread_create(&threads[t],NULL,loader,(void *)t);
if(rc)
exit(-1);
}
for(int t = 0; t < numthreads; t++){
rc = pthread_join(threads[t],&status[t]);
if(rc)
exit(-1);
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
cout << result[i][j] << " ";
}
cout << endl;
}
}

算私有,对吧,因为它是在线程初始化后创建的?这是我唯一能想到的。也许我的循环不正确,但这是我编写的第一个 pthreads 程序,所以我不确定制作嵌套 for 循环的最佳方法。

我可以看到三个正确性问题。

首先,每个线程都设置result = board没有锁定,无论如何你甚至不想每次循环都这样做。 只需让主线程执行此操作一次 - 后续迭代使用result作为其输入。

其次,这些嵌套循环:

for(long i = 0; i < n; i ++){
if(i % numthreads != thid)
continue;
for(long j = 0; j < n ; j++){
if(i % numthreads != thid)
continue;
/* ... */

意味着列行都必须与线程 ID 匹配 - 这意味着您的大多数单元格将被跳过。 例如,如果线程数为 3,则线程 0 将访问[0][0][0][3]、...线程 1 将访问[1][1].[1][4], ...但是没有线程会访问[0][1](因为该行与线程 0 匹配,列与线程 1 匹配)。

您可以通过在线程之间划分行并让一个线程处理整行来解决此问题:

for(long i = 0; i < n; i ++){
if(i % numthreads != thid)
continue;
for(long j = 0; j < n ; j++){
/* ... */

第三,每个线程在处理每个单元格后都会更新result- 这意味着一些单元格正在根据其他单元格的部分结果计算其结果,这甚至不会以确定的顺序发生,因此结果不会稳定。

您可以通过删除在loader()函数中更新result并将其放入main()lifetime循环中来解决此问题,因此游戏的每一步都只发生一次。

还有一个性能问题 - 你在游戏的每一步都启动和停止一堆线程。 这根本不会表现得很好 - 启动和停止线程是一项重量级的操作。 一旦你让它工作,你可以通过让每个线程做lifetime循环并一直保持运行来解决这个问题。 您可以使用pthread_barrier_wait()同步每一步。

最新更新