简体   繁体   English

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

[英]Pthreads - turning sequential programs into parallel

I'm simulating "Conway's Game of Life" with C++, where a 2d matrix signifies the board and a 0 is an empty cell while a 1 is a living cell. 我正在用C ++模拟“ Conway的生活游戏”,其中2d矩阵表示电路板,0表示空单元,而1表示活单元。 I originally wrote this sequentially, and tried to make it parallel with pthreads. 我最初按顺序编写此文件,并尝试使其与pthreads并行。 For some reason though, the program is no longer behaving as expected. 但是由于某种原因,该程序不再具有预期的性能。 While it goes through both loops and seems to pick up on some of the "count++"s, it doesn't pick up all of them, and thus each round the cell is evaluated as only having one or zero neighbors (even when that is not the case). 尽管它经历了两个循环并似乎吸收了一些“计数++”,但并没有吸收全部,因此该单元格的每一轮都被评估为只有一个或零个邻居(即使那是并非如此)。 This leads to the "result" after a set time period to be all zeroes, because every cell dies without being able to reproduce. 这导致在设定的时间段后的“结果”全为零,因为每个单元都死了而无法复制。 I've been working on this for a couple days and changing up different things but still can't figure it out. 我已经为此工作了几天,并做了很多不同的事情,但仍然无法解决。 Here's my code: 这是我的代码:

#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;
    }
}

Count private in this, right, because it is created after the threads are initialized? 算是私有的,对吧,因为它是在线程初始化之后创建的? That was the only thing I could think of. 那是我唯一能想到的。 Maybe my loops are done incorrectly, but this is the first pthreads program I've written so I'm not sure yet the best way to make a nested for loop. 也许我的循环执行不正确,但这是我编写的第一个pthreads程序,因此我不确定还是嵌套嵌套循环的最佳方法。

There's three correctness issues I can see immediately. 我可以立即看到三个正确性问题。

First, every thread sets result = board with no locking, and you don't even want to do that every loop anyway. 首先,每个线程都将result = board为无锁定状态,并且您甚至不想执行每个循环。 Just have the main thread do that once - subsequent iterations use result as their input. 只需让主线程执行一次操作-后续迭代就将result用作其输入。

Second, these nested loops: 其次,这些嵌套循环:

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

mean that both the column and the row have to match the thread ID - which means that most of your cells will be skipped. 表示列行都必须匹配线程ID-这意味着将跳过大多数单元格。 For example, if numthreads is 3 then thread 0 will visit [0][0] , [0][3] , ... and thread 1 will visit [1][1] . 例如,如果numthreads为3,则线程0将访问[0][0][0][3] ,...,线程1将访问[1][1] [1][4] , ... but no thread will visit [0][1] (because the row matches thread 0, and the column matches thread 1). [1][4] ,...,但是没有线程会访问[0][1] (因为该行匹配线程0,而该列匹配线程1)。

You can fix this issue by just dividing up the rows between threads and letting one thread process the entire row: 您可以通过仅在线程之间划分行并让一个线程处理整个行来解决此问题:

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

Third, every thread is updating result after every cell is processed - this means that some cells are calculating their result based on partial results from other cells, and this doesn't even happen in a deterministic order so the result won't be stable. 第三, 每个线程在处理完每个单元格之后都会更新result -这意味着某些单元格是根据其他单元格的部分结果来计算其结果的,而这甚至没有确定的顺序发生,因此结果将不稳定。

You can fix this by removing the code that updates result in the loader() function and putting that inside the lifetime loop in main() , so it just happens once for every step of the game. 您可以通过删除loader()函数中更新result的代码,并将其放入main()lifetime循环中来解决此问题,因此在游戏的每个步骤中都只会发生一次。

There's also a performance issue - you are starting up and stopping a bunch of threads every step of the game. 还有一个性能问题-您正在游戏的每个步骤中启动和停止大量线程。 That won't perform very well at all - starting and stopping threads is a heavyweight operation. 这根本不能很好地执行-启动和停止线程是一项重量级的操作。 Once you have it working, you can fix this by having each thread do the lifetime loop and stay running the whole time. 一旦工作,就可以通过让每个线程执行lifetime循环并始终保持运行来解决此问题。 You synchronise at each step using pthread_barrier_wait() . 您可以使用pthread_barrier_wait()在每一步进行同步。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM