C 将参数传递到线程中的安全方法

C 将参数传递到线程中的安全方法,c,pthreads,C,Pthreads,您能否澄清一下,为什么以下代码是将参数传递到新线程的安全方法: //Listing 5.3 Passing a Value into a Created Thread for ( int i=0; i<10; i++ ) pthread_create( &thread, 0, &thread_code, (void *)i ); //清单5.3将值传递给创建的线程 对于(int i=0;i第三种方法如下所示: static int args[10]; for (

您能否澄清一下,为什么以下代码是将参数传递到新线程的安全方法:

//Listing 5.3 Passing a Value into a Created Thread
for ( int i=0; i<10; i++ )
    pthread_create( &thread, 0, &thread_code, (void *)i );
//清单5.3将值传递给创建的线程

对于(int i=0;i第三种方法如下所示:

static int args[10];
for ( int i=0; i<10; i++ ) {
    args[i] = i;
    pthread_create( &thread, 0, &thread_code, (void *)&args[i] );
}
static int args[10];

对于(int i=0;i第三种方法如下所示:

static int args[10];
for ( int i=0; i<10; i++ ) {
    args[i] = i;
    pthread_create( &thread, 0, &thread_code, (void *)&args[i] );
}
static int args[10];
对于(inti=0;iWhy)第二个例子错了吗?
正如你的引文所说,你不能传递一个指向Interaction变量的指针,因为它会很快改变。你永远不知道并发线程何时会使用这个指针并取消引用它

// Listing 5.4 Erroneous Way of Passing Data to a New Thread
for ( int i=0; i<10; i++ )
    pthread_create( &thread, 0, &thread_code, (void *)&i );
但是
pthread\u create()
只接受
void*
(一个通用指针)。该示例使用了一个特殊的技巧,将整数值转换为指针值。线程函数将执行相反的操作(将指针转换回整数)

这种技巧通常用于在需要对象的位置存储整数值,因为它避免了分配和取消分配对象。这种技术是好是坏的做法超出了事实答案的范围。它被用于GLib之类的框架中,但我想许多程序员会对此嗤之以鼻

最后说明 本书中的示例显然不是解决实际问题的方法,而是激励示例。在实际代码中,您很少只传递一个整数值,并且您可能希望在某个时间点加入线程。因此,在一个简单的场景中,您必须分配线程参数,填写它们,启动worker,加入工作检索结果并释放分配

在一个更复杂的场景中,您将与线程通信,因此您将不局限于在创建线程时提供线程,并在加入线程后检索结果。您甚至可以让工作人员在需要时运行并重用线程。

为什么第二个示例是错误的? 正如你的引文所说,你不能传递一个指向Interaction变量的指针,因为它会很快改变。你永远不知道并发线程何时会使用这个指针并取消引用它

// Listing 5.4 Erroneous Way of Passing Data to a New Thread
for ( int i=0; i<10; i++ )
    pthread_create( &thread, 0, &thread_code, (void *)&i );
但是
pthread\u create()
只接受
void*
(一个通用指针)。该示例使用了一个特殊的技巧,将整数值转换为指针值。线程函数将执行相反的操作(将指针转换回整数)

这种技巧通常用于在需要对象的位置存储整数值,因为它避免了分配和取消分配对象。这种技术是好是坏的做法超出了事实答案的范围。它被用于GLib之类的框架中,但我想许多程序员会对此嗤之以鼻

最后说明 本书中的示例显然不是解决实际问题的方法,而是激励示例。在实际代码中,您很少只传递一个整数值,并且您可能希望在某个时间点加入线程。因此,在一个简单的场景中,您必须分配线程参数,填写它们,启动worker,加入工作检索结果并释放分配



在一个更复杂的场景中,您将与线程进行通信,因此您不会局限于在创建线程时提供线程,并在加入线程后检索结果。您甚至可以让工作人员在需要时运行并重用线程。

显然,您的答案与我在书中读到的答案大不相同。我从逻辑上得出了这个答案。如果我希望每个线程中的不同参数与主线程通信,并且不想执行可能会咬到我的类型转换,那么这符合我的目的。请注意,使用第三种方法,在新线程全部完成之前,您仍然必须确保
args
保持在范围内。@immibis是的,您是对的。如果ARGS的寿命可能比线程小,应该考虑制作这些<代码>静态< /代码>,你的答案与我在书中读到的完全不同。我从逻辑上回答了这个问题。如果我希望每个线程中的不同的ARG与主线程通信,并且不想做可能会让我咬错的类型化,那么这就是我的目的。请注意,用这第三种方法,您仍然必须确保<>代码> ARGs<代码>,直到新线程全部完成为止。@ immibIS是的,您是对的。如果ARG的生命可以小于线程,则应该考虑在代码中制作这些<代码>静态< /代码>。“将数据传递给新线程的错误方式"是否保证第一个方法中的
i
即使在循环完成后仍保持活动状态?让我们说是的。在启动所有线程后,连接所有线程是合理的。这不是让变量是否活动,而是由“C”规范指导。在您的情况下,它应该是C99或C11。您的书本身说
 除非堆栈肯定存在
@MohitJain:在第一种方法中,你根本不依赖变量是否仍然存在。在书中,第二种方法是“将数据传递给新线程的错误方式”是否保证第一个方法中的
i
即使在循环完成后仍保持活动状态?让我们说是的。在启动所有线程后,连接所有线程是合理的。这不是让变量是否活动,而是由“C”规范指导。在您的情况下,它应该是C99或C11。您的书本身说
 除非堆栈肯定存在
@MohitJain:在第一种方法中,你根本不依赖于变量是否仍然存在。Pavel,谢谢你给出了令人惊讶的答案!我被
(void*)
部分欺骗了。现在我似乎很清楚,它只是
I
的实际值的容器。然而,
(void*)i
不适用于非整数
i
,对吗?@Konstantin:Ordinal类型,它基本上就是你可以