我正在寻找一个非常简单的例子来演示使用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'(无效参数),因为在调用thread2pthread_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'(无效参数),因为调用thread2pthread_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 slicessequencing有关。当第一个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;
}

希望这能有所帮助。

10-06 09:23