为什么这段代码导致死锁?

Laz*_*zer 3 c++ deadlock pthreads cilk cilk-plus

我很惊讶地看到pstack这段代码导致死锁!我没有看到同样的理由.

pthread_mutex_t lock;

_Cilk_for (int i = 0; i < N; ++i) {
  int ai = A[i];
  if (ai < pivot) {
    pthread_mutex_lock(&lock);
    A[ia++] = ai;
    pthread_mutex_unlock(&lock);
  }
  else if (ai > pivot) {
    pthread_mutex_lock(&lock);
    A[ib++] = ai;
    pthread_mutex_unlock(&lock);
  }
  else {
    pthread_mutex_lock(&lock);
    A[ic++] = ai;
    pthread_mutex_unlock(&lock);
  }
}
Run Code Online (Sandbox Code Playgroud)

我只是使用互斥锁来确保对A的访问是原子的和序列化的.

  • 这段代码导致死锁有什么问题?
  • 有没有更好的方法来实现这个?

pax*_*blo 5

如果函数内部有代码,则表示您没有正确初始化互斥锁.您需要将其设置为PTHREAD_MUTEX_INITIALIZER(对于简单的默认互斥锁)或对其执行操作pthread_mutex_init()(对于更复杂的要求).如果没有正确的初始化,你就不知道互斥体的起始状态 - 它很可能处于锁定状态,因为发生在该位置的堆栈上看起来就像一个锁定的互斥锁.

这就是为什么它总是需要以某种方式初始化,以便初始状态毫无疑问.

您可能遇到的另一个潜在问题是:

int ai = A[i];
Run Code Online (Sandbox Code Playgroud)

可能应该使用相同的互斥锁保护该访问权限,否则您可能会以"半状态"读取它(当另一个线程只是更新变量的一部分时).


而且,我不得不说,我不确定在这里明智地使用线程.互斥体的使用可能会淹没一个声明,就像A[ia++] = ai绝大多数时间用于锁定和解锁互斥锁一样.在锁定期间处理的代码更加实用的情况下,它们通常更有用.

你可能会发现一个非线程的变种会将这个变为水中(但是,当然,不要接受我的话 - 我的主要优化口号是"测量,不要猜测").