6

我正在使用 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 循环的情况下克服这个问题?

谢谢

4

5 回答 5

4

发生这种情况是因为一旦您将指针传递给您,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;
}

[建议:避免 C 风格的演员表。]

于 2013-04-19T02:23:35.427 回答
3

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

至少有3个解决方案:

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

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

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

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

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

int id = (pthread_t *)ptr - p;
于 2013-04-19T02:50:43.663 回答
2

你让这有点太复杂了:

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

您只想传递值,而不是指向它的指针,所以 pass (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;
}
于 2013-04-19T02:28:05.320 回答
1

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

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

int args[count];

for (int i = 0; i < count; i++){
     args[i]=i;
     pthread_create(&p[i], NULL, &somefunc, (void*)args[i]);
}
于 2014-02-25T08:37:28.297 回答
-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);
}
于 2021-01-24T01:24:42.420 回答