pthread_join 死锁的简单示例

Simple example for pthread_join deadlock

我正在寻找一个非常简单的示例来演示使用 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'(无效参数),因为在调用 thread1pthread_create 时尚未指定 thread2

有什么想法吗?

如果您只是想证明 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;
}

这将导致主线程在派生线程上等待,而派生线程在主线程上等待(导致死锁),而不需要额外的锁定原语来扰乱您要演示的内容。

并更直接地回答您的一些问题:

it says 'EINVAL' (invalid argument) because thread2 is not yet specified when pthread_create for thread1 is called.

...以及您的评论之一...

I tried this and it worked, but the problem is, it only works SOMETIMES because sometimes I get EINVAL again.

在您的代码中,您连续调用 pthread_create 以生成 2 个线程:

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 allocated to each thread's context and sequencing 有关。当第一个 pthread_create 被调用时,您将在 returns 之后有一个 thread1 的有效句柄,但 thread2 的句柄还无效,至少在第二个 pthread_create 被调用。

为此,当一个线程被创建时,线程的行为"alive"(即线程函数实际上运行)可能需要一些额外的时间,即使返回的线程句柄是有效的.在这些情况下,一个线程执行的代码可能比 "expected" 多。在你的代码中,两个 pthread_create 函数 可能 恰好在分配给主线程的时间片中被调用, 可以 给每个在命中 pthread_join 允许 tid_c 指向有效句柄的语句之前生成足够多的线程 "time";在 EINVAL 的情况下,调用 pthread_create(&thread1, NULL, joinit, &thread2) 并且生成的线程在 pthread_create(&thread2, NULL, joinit, &thread1) 之前命中 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;
}

希望对您有所帮助。

错误的主要原因是您有两个线程,每个线程都在等待同一个线程终止,因为在 main 中调用了 pthread_join。另一个问题是您不能确保每个线程都能正确看到另一个线程的 ID。

像这样修复它:

#include <stdio.h>
#include <pthread.h>

pthread_t thread1;
pthread_t thread2;
pthread_mutex_t mutex;
pthread_cond_t cond;
int go = 0;


void* joinit(void* ptr)
{
  // wait until both thread IDs are known
  pthread_mutex_lock(&mutex);
  while (go == 0)
      pthread_cond_wait(&cond, &mutex);
  pthread_mutex_unlock(&mutex);

  pthread_t* tid_c = *((pthread_t**) ptr);
  printf("About to wait\n");
  int retval = pthread_join(*tid_c, NULL);
  printf("In joinit: tid = %d, retval = %d \n", *tid_c, retval);

  // tell the other threads we're done
  pthread_mutex_lock(&mutex);
  go++;
  pthread_cond_broadcast(&cond);
  pthread_mutex_unlock(&mutex);

  return NULL;
}

int main()
{
  // setup synchronization
  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init(&cond, NULL);

  pthread_create(&thread1, NULL, joinit, &thread2);
  pthread_create(&thread2, NULL, joinit, &thread1);

  // tell the threads to go
  pthread_mutex_lock(&mutex);
  go = 1;
  pthread_cond_broadcast(&cond);
  pthread_mutex_unlock(&mutex);

  // wait for both threads to finish
  pthread_mutex_lock(&mutex);
  while (go != 3)
      pthread_cond_wait(&cond, &mutex);
  pthread_mutex_unlock(&mutex);

  return 0;
}