我正在寻找一个非常简单的例子来演示使用pthread_join
的死锁;然而,这并不是一件小事。
我从这个开始:
void* joinit(void* tid)
{
pthread_t* tid_c = (pthread_t*)tid;
int retval = pthread_join(*tid_c, NULL);
printf("In joinit: tid = %d, retval = %d \n", *tid_c, retval);
return NULL;
}
int main()
{
pthread_t thread1;
pthread_t thread2;
pthread_create(&thread1, NULL, joinit, &thread2);
pthread_create(&thread2, NULL, joinit, &thread1);
pthread_join(thread2, NULL);
return 0;
}
但是,它说'EINVAL'(无效参数),因为在调用
thread2
的pthread_create
时尚未指定thread1
。有什么想法吗?
最佳答案
如果您只是想演示pthread_join
可能导致死锁,可以执行类似于以下代码的操作:
#include <stdio.h>
#include <pthread.h>
void* joinit(void* tid)
{
printf("In %#x, waiting on %#x\n", pthread_self(), (*((pthread_t*)tid)));
pthread_join((*((pthread_t*)tid)), NULL);
printf("Leaving %#x\n", pthread_self());
return NULL;
}
int main(void)
{
pthread_t thread1 = pthread_self();
pthread_t thread2;
pthread_create(&thread2, NULL, joinit, &thread1);
joinit(&thread2);
return 0;
}
这将导致主线程在生成的线程上等待,而生成的线程在主线程上等待(导致保证的死锁),而不需要额外的锁定原语来干扰您试图演示的内容。
更直接地回答你的一些问题:
它说'EINVAL'(无效参数),因为调用
thread2
的pthread_create
时尚未指定thread1
。... 从你的评论中。。。
我试过了,但问题是,这只是有时工作,因为有时我得到了艾因瓦尔。
在您的代码中,您连续调用
pthread_create
来生成两个线程:pthread_create(&thread1, NULL, joinit, &thread2);
pthread_create(&thread2, NULL, joinit, &thread1);
在
joinit
代码中,获取传入的线程句柄以加入:pthread_t* tid_c = (pthread_t*)tid;
int retval = pthread_join(*tid_c, NULL);
有时这会起作用,而您将得到
EINVAL
的其他原因与分配给每个线程上下文的time slices和sequencing有关。当第一个pthread_create
被调用时,在它返回之后您将拥有一个有效的thread1
句柄,但是thread2
的句柄仍然无效,至少在调用第二个pthread_create
之前是这样。为此,在创建线程时,线程“活跃”的行为(即线程函数实际运行)可能需要一些额外的时间,即使返回的线程句柄是有效的。在这些情况下,一个线程可能执行的代码比“预期”的多。在您的代码中,两个
pthread_create
函数可能都是在为主线程分配的时间片中调用的,该时间片在命中允许pthread_join
指向有效句柄的tid_c
语句之前为每个派生线程提供足够的“时间”;在EINVAL
情况下,调用了pthread_create(&thread1, NULL, joinit, &thread2)
,生成的线程在pthread_join(*tid_c, NULL)
给pthread_create(&thread2, NULL, joinit, &thread1)
一个有效句柄(导致错误)之前命中了thread2
。如果您希望保持代码与现在的代码类似,则需要添加某种锁以确保线程不会过早退出或调用任何东西:
#include <stdio.h>
#include <pthread.h>
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void* joinit(void* tid)
{
/* this can be above the lock because it will be valid after lock is acquired */
pthread_t* tid_c = (pthread_t*)tid;
int retval = -1;
pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);
printf("%#x waiting on %#x\n", pthread_self(), *tid_c);
retval = pthread_join(*tid_c, NULL);
printf("In joinit: tid = %d, retval = %d \n", *tid_c, retval);
return NULL;
}
int main()
{
pthread_t thread1;
pthread_t thread2;
/* get the lock in the main thread FIRST */
pthread_mutex_lock(&lock);
pthread_create(&thread1, NULL, joinit, &thread2);
pthread_create(&thread2, NULL, joinit, &thread1);
/* by this point, both handles are "joinable", so unlock */
pthread_mutex_unlock(&lock);
/* can wait on either thread, but must wait on one so main thread doesn't exit */
pthread_join(thread2, NULL);
return 0;
}
希望这能有所帮助。