【问题标题】:How can I pass the index of a for loop as the argument for pthread_create如何将 for 循环的索引作为 pthread_create 的参数传递
【发布时间】:2013-04-12 08:20:29
【问题描述】:

我正在使用 for 循环创建多个线程并将索引 i 作为参数传递如下:

pthread_t p[count];
for (int i = 0; i < count; i++){
    pthread_create(&p[i], NULL, &somefunc, (void*)&i);
}

然后我尝试检索 i 的值:

void *somefunc (void* ptr){
    int id = *(int*)ptr;
}

但是,我注意到有时,线程中的 id 会有重叠的值,我怀疑这是由于在线程能够检索值之前更新 for 循环的索引(因为我传入了指针,而不是值本身)。有没有人有任何建议可以在不减慢 for 循环的情况下克服这个问题?

谢谢

【问题讨论】:

    标签: c multithreading pthreads


    【解决方案1】:

    您可以在 main 函数的开头声明一个任务 id 数组。每当您创建线程时,您都可以发送该数组的索引以防止参数被覆盖。

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    #define NUM_THREADS 8
    
    void *PrintHello(void *threadid)
    {
       long taskid;
       sleep(1);
       taskid = (long) threadid;
       printf("Hello from thread %ld\n", taskid);
       pthread_exit(NULL);
    }
    
    int main(int argc, char *argv[])
    {
       pthread_t threads[NUM_THREADS];
       long taskids[NUM_THREADS];
       int rc;
    
       for(long t=0;t<NUM_THREADS;t++) {
          printf("Creating thread %ld\n", t);
          taskids[t] = t;
          rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
          
          if (rc) {
             printf("ERROR; return code from pthread_create() is %d\n", rc);
             exit(-1);
             }
       }
    
       pthread_exit(NULL);
    }
    

    【讨论】:

    • 难怪你不理解我的回答并否决了我的回答——你甚至不理解自己的回答。 (void *) taskids[t] 只是将您存储在taskids[t] 中的值转换为void*,因此您可以传递(void*)tgot the exact same behaviour,而根本没有taskids 数组。我的答案和你的答案之间唯一真正的区别是你选择转换为long,而我使用的是int,你的主函数不应该调用pthread_exit——它应该加入它创建的线程,所以main()不' t 在他们之前终止。
    【解决方案2】:

    我认为最好的答案是在开头声明一个 args 数组,其大小与您打算创建的线程数量相同。

    这样,这些值永远不会被覆盖或处于竞争状态。

    int args[count];
    
    for (int i = 0; i < count; i++){
         args[i]=i;
         pthread_create(&p[i], NULL, &somefunc, (void*)args[i]);
    }
    

    【讨论】:

      【解决方案3】:

      正如其他人所说,您将指针传递给另一个线程(父线程)正在修改的对象并在没有任何同步的情况下访问它。这是一个错误。

      至少有3个解决方案:

      1. 为单个int 分配空间(通过C++ 中的new 或C 中的malloc),并让新线程负责释放它。这可能是最糟糕的解决方案,因为现在您有一个额外的失败案例需要处理(分配失败),因此它会使您的代码复杂化和混乱。

      2. 将整数转换为 void * 并返回。这肯定会在任何现实世界的 POSIX 系统上工作,但它并不能“保证”工作,也许更烦人的是,它可能会引发警告。您可以通过uintptr_t 进行中间转换来避免警告。

      3. 传递地址而不是索引:

        pthread_create(&p[i], NULL, &somefunc, &p[i]);

      然后 start 函数可以通过减去p 来恢复索引(如果它需要它):

      int id = (pthread_t *)ptr - p;
      

      【讨论】:

      • +1 用于列举具有优缺点的选项。 3 很聪明,但启动的功能可能无法访问p(我什至可以说他们通常不应该知道或依赖于他们的创建者的这些细节)。
      • 我同意,但 (3) 似乎确实适用于 OP 的情况。顺便说一句,如果索引的数量很少,您可以使用相同的原理,但使用一个虚拟数组:static const char dummy[100];,然后将&amp;dummy[i] 传递给 start 函数作为传递i 的迂回方式。
      【解决方案4】:

      这有点太复杂了:

      for (int i = 0; i < count; i++){
          pthread_create(&p[i], NULL, &somefunc, (void*)&i);
      

      你只想传递值,而不是指向它的指针,所以传递(void*)i。照原样,您正在向每个线程传递一个指向有问题的i 的指针:

      • i 可能会在线程尝试从其地址读取时离开作用域 - 其他变量可能会在那里,或者内存可能会闲置而未使用,而谁知道其中还剩下什么
      • 循环中的下一次迭代无论如何都会覆盖该值,这意味着所有线程在取消引用指针时都可能会看到值“count”,如果它没有像上面那样被破坏,除了极少数情况启动线程在循环期间暂停,允许它产生的线程读取一些较早的i

      所以:

      for (int i = 0; i < count; i++){
          pthread_create(&p[i], NULL, &somefunc, (void*)i);
      
      ...
      void *somefunc (void* id_cast_to_voidptr){
          int id = (int)id_cast_to_voidptr;
      }
      

      【讨论】:

      • 我收到了一些关于 (void*)i 和 (int)id_cast_to_voidptr 的强制转换警告/错误。
      • 通过uintptr_t 使用中间转换来消除警告。
      • 这似乎是一样的。在 for 循环中初始化变量没有区别。这绝对适用于 JS,但不适用于 C
      • @CarlosEmilianoCastroTrejo:“这似乎是一样的。” - what 似乎与 what 相同?如果您的意思是代码的顶部 sn-p 与问题中的相同 - 是的,故意的,所以我可以将它放在那里以便于参考,同时解释它为什么不起作用。 “在 for 循环中初始化变量没有区别。” - 正确 - for 循环没有任何问题,所以没有理由做任何不同。你说的没有任何意义。当然它适用于 C。
      【解决方案5】:

      发生这种情况是因为一旦您将指针传递给i,您现在就有多个线程使用相同的值。这会导致数据竞争,因为第一个线程正在修改i,而您的第二个线程希望它永远不会改变。您始终可以分配一个临时 int 并将其传递给线程函数。

      pthread_create(&p[i], NULL, &somefunc, new int(i));
      

      这将在动态存储(堆)中分配一个整数并使用i 的值对其进行初始化。然后将指向新分配的整数的指针传递给线程函数。

      然后在线程函数中,你可以像以前一样获取传递的值,然后删除 int 对象。

      void *somefunc (void* ptr){
          int id = *(int*)ptr;
          delete (int*)ptr;
      }
      

      [建议:Avoid C style casts.]

      【讨论】:

        猜你喜欢
        • 2017-06-20
        • 1970-01-01
        • 1970-01-01
        • 2014-05-28
        • 1970-01-01
        • 1970-01-01
        • 2021-11-20
        • 2023-03-12
        • 1970-01-01
        相关资源
        最近更新 更多