线程是否可以在不锁定的情况下写入相同结构数组的不同元素?

时间:2011-03-15 18:37:31

标签: c multithreading gcc thread-safety

我正在尝试在GCC C应用程序中使用线程(第一次!),它在非线程模式下工作正常。当我运行它时,一些线程给出的结果都是零,而不是所需的答案(我知道用于检查目的),但是每次运行它时给出零的线程都不一样。给出非零答案的答案是正确的,因此代码似乎运行正常。我想知道是否有人可以指出我可能有一些非线程安全的区域。

我自己的想法可能是由于我如何收集结果或内存分配 - 我使用malloc和free而在StackOverflow中的其他地方我看到GCC malloc被认为是线程安全的,如果与-lpthread链接(我正在做)。没有什么可以使用全局/静态变量 - 所有东西都作为函数参数传递。

为了将结果传递回main,我的线程例程使用了一组结构。每个线程都写入此数组的不同元素,因此它们不会尝试写入同一个内存。也许我在编写结果时需要使用某种形式的锁定,即使它们没有转到结构数组的相同元素?

我在这里遵循了线程代码的配方: https://computing.llnl.gov/tutorials/pthreads/#Abstract

我附加(简化)代码提取,以防这提供任何线索(我可能已经错误地删除/修改了某些内容,但我并不是要求任何人发现错误,只是一般方法)。

typedef struct p_struct { /* used for communicating results back to main */
    int given[CELLS];
    int type;
    int status;
    /*... etc */
} puzstru;

typedef struct params_struct { /* used for calling generate function using threads */
    long seed;
    char *text;
    puzzle *puzzp;
    bool unique;
    int required;
} paramstru;
/* ========================================================================================== */
void *myfunc(void *spv) /* calling routine for use by threads */
{
    paramstru *sp=(paramstru *)spv;
    generate(sp->seed, sp->text, sp->puzzp, sp->unique, sp->required);
    pthread_exit((void*) spv);
}
/* ========================================================================================== */
int generate(long seed, char *text, puzstru *puzzp, bool unique, int required)
{
/* working code , also uses malloc and free,
    puts results in the element of a structure array pointed to by "puzzp", 
    which is different for each thread
    (see calling routine below :        params->puzzp=puz+thr; )
    extract as follows: */
            puzzp->given[ix]=calcgiven[ix];
            puzzp->type=1; 
            puzzp->status=1;
            /* ... etc */
}
/* ========================================================================================== */


int main(int argc, char* argv[])
{
    pthread_t thread[NUM_THREADS];
    pthread_attr_t threadattr;
    int thr,threadretcode;
    void *threadstatus;
    paramstru params[1];

    /* ....... ETC */

/* set up params structure for function calling parameters */
    params->text=mytext;
    params->unique=TRUE;
    params->required=1;

    /* Initialize and set thread detached attribute */
    pthread_attr_init(&threadattr);
    pthread_attr_setdetachstate(&threadattr, PTHREAD_CREATE_JOINABLE);

    for(thr=0; thr<NUM_THREADS; thr++)
    {
        printf("Main: creating thread %d\n", thr);
        params->seed=ran_arr_next(startingseeds); 
        params->puzzp=puz+thr;
        threadretcode = pthread_create(&thread[thr], &threadattr, myfunc, (void *)params); 
        if (threadretcode)
        {
            printf("ERROR; return code from pthread_create() is %d\n", threadretcode);
            exit(-1);
        }
    }

    /* Free thread attribute and wait for the other threads */
    pthread_attr_destroy(&threadattr);
    for(thr=0; thr<NUM_THREADS; thr++)
    {
        threadretcode = pthread_join(thread[thr], &threadstatus);
        if (threadretcode)
        {
            printf("ERROR; return code from pthread_join() is %d\n", threadretcode);
            exit(-1);
        }
        printf("Main: completed join with thread %d having a status of %ld\n",thr,(long)threadstatus);
    }

/* non-threaded code, print results etc ............. */

    free(startingseeds);
    free(puz);
    printf("Main: program completed. Exiting.\n");
    pthread_exit(NULL);
}

为了其他人阅读本文的好处 - 所有答案都是正确的,标题中问题的答案是YES,线程可以安全地写入同一个结构数组的不同元素,我的问题在于调用例程 - 以下是修改后的代码片段(现在工作正常):

    paramstru params[NUM_THREADS];

    for(thr=0; thr<NUM_THREADS; thr++)
    {
        printf("Main: creating thread %d\n", thr);
    /* set up params structure for function calling parameters */
        params[thr].text=mytext;
        params[thr].unique=TRUE;
        params[thr].required=1;
        params[thr].seed=ran_arr_next(startingseeds); 
        params[thr].puzzp=puz+thr;
        threadretcode = pthread_create(&thread[thr], &threadattr, myfunc, (void *)&params[thr]); 
        if (threadretcode)
        {
            printf("ERROR; return code from pthread_create() is %d\n", threadretcode);
            exit(-1);
        }
    }

3 个答案:

答案 0 :(得分:9)

要回答你的问题,在不锁定的情况下从不同的线程写入同一数组的不同元素是完全可以的。如果两个线程在没有同步(例如,锁定)的情况下写入相同的字节,则只会有data race

正如其他答案所指出的那样,你的代码写入中断的原因是你将一个指向同一个params对象的指针传递给你的每个线程,然后修改该对象。您可能希望为每个线程创建一个新的param

答案 1 :(得分:3)

paramstru params[1];

代码将相同的结构传递给所有线程。只是线程初始化循环会覆盖线程应该处理的数据:

for(thr=0; thr<NUM_THREADS; thr++)
    {
        printf("Main: creating thread %d\n", thr);
        params->seed=ran_arr_next(startingseeds); /* OVERWRITE  */
        params->puzzp=puz+thr; /* OVERWRITE  */

非线程代码的工作原因是因为每次调用myfunc()都会在params结构发生更改之前终止。

答案 2 :(得分:1)

您只创建了一个参数结构的副本,并覆盖它并将相同的地址传递给每个线程。不要'你想要paramstru params[NUM_THREADS];