简体   繁体   English

如何将 for 循环的索引作为 pthread_create 的参数传递

[英]How can I pass the index of a for loop as the argument for pthread_create

I am using a for loop to create a number of threads and passing the index i as an argument as follows:我正在使用 for 循环来创建多个线程并将索引 i 作为参数传递,如下所示:

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

Then I attempt to retrieve the value of i:然后我尝试检索 i 的值:

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

However, I noticed that sometimes, id in the threads will have overlapping values which I suspect is due to the index of the for loop updating before the thread is able to retrieve the value (since I passed in the pointer, as opposed to the value itself).但是,我注意到有时,线程中的 id 会有重叠的值,我怀疑这是由于在线程能够检索值之前更新了 for 循环的索引(因为我传入了指针,而不是值本身)。 Does anyone have any suggestions to overcome this issue without slowing down the for loop?有没有人有任何建议可以在不减慢 for 循环的情况下克服这个问题?

Thanks谢谢

This is happening because once you pass a pointer to i you now have multiple threads using the same value.发生这种情况是因为一旦您将指针传递给i您现在就有多个线程使用相同的值。 This causes a data race because the first thread is modifying i and your second thread is expecting it to never change.这会导致数据竞争,因为第一个线程正在修改i而您的第二个线程期望它永远不会改变。 You can always allocate a temporary int and pass it to the thread function.您始终可以分配一个临时 int 并将其传递给线程函数。

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

This will allocate an integer in dynamic storage (heap) and initialize it with the value of i .这将在动态存储(堆)中分配一个整数,并使用i的值对其进行初始化。 A pointer to the newly allocated integer will then be passed to the thread function.然后将指向新分配的整数的指针传递给线程函数。

Then in the thread function you can take the value passed as you already do and then delete the int object.然后在线程函数中,您可以像已经做的那样获取传递的值,然后删除 int 对象。

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

[Suggestion: Avoid C style casts. [建议: 避免 C 风格的强制转换。 ] ]

As others have said, you're passing a pointer to an object that's being modified by another thread (the parent) and accessing it without any synchronization.正如其他人所说,您正在传递一个指向正在被另一个线程(父线程)修改的对象的指针,并在没有任何同步的情况下访问它。 This is an error.这是一个错误。

There are at least 3 solutions:至少有3种解决方案:

  1. Allocate (via new in C++ or malloc in C) space for a single int , and have the new thread be responsible for freeing it.为单个int分配(通过 C++ 中的new或 C 中的malloc )空间,并让新线程负责释放它。 This is probably the worst solution because now you have an extra failure case to handle (failure to allocate) and thus it complicates and clutters your code.这可能是最糟糕的解决方案,因为现在您有一个额外的失败案例要处理(分配失败),因此它使您的代码复杂化和混乱。

  2. Cast the integer to void * and back.将整数转换为void *并返回。 This will surely work on any real-world POSIX system, but it's not "guaranteed" to work, and perhaps more annoyingly, it may incur warnings.这肯定会在任何现实世界的 POSIX 系统上工作,但它不能“保证”工作,也许更烦人的是,它可能会引起警告。 You can avoid the warnings with an intermediate cast through uintptr_t .您可以通过uintptr_t使用中间uintptr_t来避免警告。

  3. Instead of passing an index, pass an address:不传递索引,而是传递地址:

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

Then the start function can recover the index (if it needs it for anything) by subtracting p :然后 start 函数可以通过减去p来恢复索引(如果它需要它):

int id = (pthread_t *)ptr - p;

You've made this a little too complicated:你让这有点太复杂了:

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

You just want to pass the value, not a pointer to it, so pass (void*)i .您只想传递值,而不是指向它的指针,因此传递(void*)i As is, you're passing each thread a pointer to i which has problems:照原样,您正在向每个线程传递一个指向i的指针,该指针存在问题:

  • i will likely have left scope by the time the thread tries to read from its address - some other variable might be there instead or the memory might be sitting around unused with who-knows-what left in it当线程尝试从其地址读取时, i可能已经离开了范围 - 可能存在其他一些变量,或者内存可能处于未使用状态,不知道还剩下什么
  • the next iteration in the loop will overwrite the value anyway, which means all threads would be likely to see the value "count" when they dereference the pointer, if it hadn't been clobbered as above, and except for rare cases where the launching thread is suspended during looping allowing a thread it spawned to read some earlier i value循环中的下一次迭代无论如何都会覆盖该值,这意味着所有线程在取消引用指针时都可能会看到值“count”,如果它没有像上面那样被破坏,并且启动的罕见情况除外线程在循环期间暂停,允许它产生的线程读取一些较早的i

So:所以:

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;
}

I think the best answer is to declare an array of args at the beginning, with the same size as the amount of threads you intend to create.我认为最好的答案是在开始时声明一个 args 数组,其大小与您打算创建的线程数量相同。

That way those values are never overwritten or in a race condition.这样,这些值永远不会被覆盖或处于竞争状态。

int args[count];

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

You can declare an array of tasks id at the beginning of the main function.您可以在 main 函数的开头声明一个任务 id 数组。 Whenever you create the thread you can send the index of that array to prevent the overtwriting of the parameter.无论何时创建线程,您都可以发送该数组的索引以防止覆盖参数。

#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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM