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

Rus*_*llG 13 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);
}
Run Code Online (Sandbox Code Playgroud)

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

    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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

Kar*_*tan 9

要回答你的问题,在不锁定的情况下从不同的线程写入同一数组的不同元素是完全正常的.如果两个线程在没有同步的情况下写入同一个字节(例如,锁定),则只会发生数据竞争.

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

  • 虽然它是安全的,但如果不仔细,可能会导致性能不佳.如果多个线程继续访问同一缓存行上的数组元素,那么您将有大量的缓存行 - 弹跳,这很昂贵. (5认同)

Apa*_*ala 3

paramstru params[1];
Run Code Online (Sandbox Code Playgroud)

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

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  */
Run Code Online (Sandbox Code Playgroud)

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