使用 pthread 打印二维数组

Using pthread to print 2-d array

所以我有一个作业说我已经创建了一个二维数组[5][12],其随机值介于 1-99 之间。然后使用 pthreads,我必须对数组中的每个元素加 1 或减 1,然后打印结果并将进程分成 2、3 或 4 个线程。线程数取决于用户在命令行中输入的内容。我有编译和运行的代码。但是,我想要的输出仅在输入数字 3 时打印。你们能告诉我我的代码哪里出错了吗?我一开始就无法理解 pthreads。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <pthread.h>
#include <iostream>

using namespace std;
int list[5][12];
int rows = 5;
int cols = 12;
int threadc;

void *threadf(void *arg)
{
    int x = (int) arg;
    for(int i = (x*60)/threadc; i < ((x+1) * 60)/threadc; i++)
    {
        for(int j = 0; j < 12; j++)
        {
            if (list[i][j] % 2 == 0)
                list[i][j] += 1;
            else
                list[i][j] -= 1;
        }
    }
}

void cArray()
{
    srand(time(NULL));
    for(int i = 0; i < 5; i++)
    {
        for(int j = 0; j < 12; j++)
        {
            list[i][j] = rand() % 99 + 1;
        }
    }

}

void pArray(int list[][12], int rows, int cols)
{
    cout << "\n";
    for(int i = 0; i < rows; i++)
    {
        for(int j = 0; j < cols; j++)
        {
            cout << list[i][j] << " ";
        }
        cout << "\n";
    }
 }

int main(int argc, char *argv[])
{
    if(argc != 2) exit(0);
    threadc = atoi(argv[1]);
    assert(threadc >= 2 && threadc <=4);
    pthread_t *thread;
    thread = new pthread_t [threadc];
    if(thread == NULL)
        exit(0);
    cArray();
    cout << "2-d Array: ";
    pArray(list, rows, cols);
    int t;
    for(int i = 0; i < threadc; i++)
    {
        t = pthread_create(&thread[i], NULL, threadf, (void *)i);
        if (t != 0)
            return 1;
    }
    for(int i = 0; i < threadc; i++)
    {
        t = pthread_join(thread[i], NULL);
        if(t != 0)
            return 1;
    }  
    cout << "Modified 2-d Array: ";
    pArray(list, rows, cols);
    return 0;
}

当创建的线程数为 2 时,thread_join return 出现错误代码(22)。如果删除 return 中的语句第二个循环,您的程序打印最终输出。

for(int i = 0; i < threadc; i++)
{
    t = pthread_join(thread[i], NULL);
    if (t != 0) 
        return 1; // <- your program works if you comment out this.


}

根据这个link:http://minirighi.sourceforge.net/html/errno_8h.html 22 是 EINVAL,表示 'thread is un-joinable'.

既然你关心return值pthread_join,我建议在你的thredf末尾添加一个成功终止函数(pthread_exit(NULL);)。此外,为了避免 @user4581301 提到的缓冲区溢出,您可以传递一个指向数据的指针。

所以 thredf 就像

void *threadf(void *arg)
{
    cout << endl;
    int x = *((int*)arg); // <- NOTICE HERE!
    for(int i = (x*60)/threadc; i < ((x+1) * 60)/threadc; i++)
      //...
    }
    pthread_exit(NULL); // <- NOTICE HERE!
}

主要的:

int main(int argc, char *argv[])
{
    if(argc != 2) exit(0);
    threadc = atoi(argv[1]);
    assert(threadc >= 2 && threadc <=4);
    pthread_t *thread;
    thread = new pthread_t [threadc];
    int *data = new int[threadc]; // <- NOTICE HERE!

    if(thread == NULL)
        exit(0);
    cArray();
    cout << "2-d Array: ";
    pArray(list, rows, cols);
    int t;
    for(int i = 0; i < threadc; i++)
    {
        data[i] = i;
        //                                                NOTICE HERE!
        t = pthread_create(&thread[i], NULL, threadf, (void *)(&data[i])); 
        if (t != 0)
            return 1;
    }
    // ...

让我们看一下 threadf 中 x = 0 和 threadc = 4 的外部 for 循环

    for(int i = (0*60)/4; i < ((0+1) * 60)/4; i++)
    for(int i = 0; i < (1 * 60)/4; i++)
    for(int i = 0; i < 60/4; i++)
    for(int i = 0; i < 15; i++)

i 的范围从 0 到 14。我被这样使用:list[i][j],所以考虑写入 list[14][11] 的位置。超出 int list[5][12]; 定义的界限就会发生坏事。未定义的行为,所以从技术上讲没有人知道会发生什么。不过我们可以做出一些很好的猜测。

int list[5][12];
int rows = 5; // probably overwritten by write to list[6][0]
int cols = 12; // probably overwritten by write to list[6][1]
int threadc; // probably overwritten by write to list[6][3]

于是rowcolumn动了,但没人在意。代码从不使用它们。但是 threadc...到处都在使用它。事实上,它被用在循环退出条件中。这里可能会发生更多的坏事。它还确定要创建和加入的线程数。某些线程可能不会创建。该程序可能会尝试加入比现有更多的线程。

无论如何,未定义的行为。我想我们都应该为编译器没有生成命令进行战术核打击的代码而感到高兴。由于这是一个家庭作业问题,我不打算解开数学 OP 要求使他们的 for 循环正确地跨多个线程分配工作,但会建议他们考虑将数组视为一维数组大小 5*12 并在单个 for 循环中进行 1D->2D 索引。

其他说明:

对于 main 中的 ithreadf 中的 x 使用 uintptr_t 而不是 intuintptr_t 保证转换为 void *

为循环计数器和数组索引器使用像 size_t 这样的无符号变量。它们与 uintptr_t 配合得很好,您几乎永远不需要负数数组索引。

使用 std::vector 而不是指针,new 用于线程列表。如果您必须使用 new 和指针,请记住在完成后删除列表。

看看是否可以使用 std::thread 代替 pthread

threadf 中添加一个 return