【问题标题】:Using conditional variables to unblock one thread but wouldn't mutex lock cause a deadlock?使用条件变量解锁一个线程但互斥锁不会导致死锁?
【发布时间】:2021-10-16 09:43:03
【问题描述】:

基本问题,但为了简洁起见,我有两个线程bar 在特定条件下解除阻塞foo,但即使程序运行良好令我惊讶,如果foo 不应该导致死锁首先运行它获取锁,这意味着bar 不应该能够继续进行,因为条件变量在foo 中永远不会为真?

pthread_mutex_t lock;
pthread_cond_t cv;
bool dataReady = false;

void foo(void *arg)
{
    printf ("Foo...\n");
    pthread_mutex_lock(&lock);
    
    while (!dataReady)
    {   
        pthread_cond_wait(&cv, &lock);
    }
    printf ("Foo unlocked...\n");
    dataReady = true;
    
    pthread_mutex_unlock(&lock);
}

void bar(void *arg)
{
   printf ("Bar...\n");
   pthread_mutex_lock(&lock);
   sleep(3);
   printf ("Data ready...\n");
   dataReady = true;
   pthread_cond_broadcast(&cv);
   
   pthread_mutex_unlock(&lock);
}

int main(void)
{

int main() 
{
    pthread_t t1,t2;
    pthread_create(&t1,NULL,foo,NULL);
    pthread_create(&t2,NULL,bar,NULL);
    
    
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    
    return 0;
}

同样在这种情况下,使用信号量没有意义是吗?

【问题讨论】:

  • "if foo is first run" 我不相信您的代码可以保证foo 将首先获得锁。
  • 有一个“如果”。我从来没有谈论过保证,操作系统也不保证。我说它可能首先发生

标签: c multithreading posix


【解决方案1】:

pthread_cond_wait(&cv, &lock); 在调用时自动释放互斥锁,并在唤醒时重新获取它。

来自man 3 pthread_cond_wait

这些函数原子地释放互斥体并导致调用线程 阻塞条件变量 cond; atomically 这里的意思是 “关于另一个线程访问互斥锁的原子性和 那么条件变量”。也就是说,如果另一个线程能够 在即将阻塞的线程释放后获取互斥锁, 然后随后调用 pthread_cond_broadcast() 或 该线程中的 pthread_cond_signal() 应表现得好像它已发出 在即将阻塞的线程被阻塞之后。

成功返回后,互斥体应已被锁定并应 由调用线程拥有。

恕我直言,C++ 文档包含更清晰的解释(我知道语言不同,但操作原理保持不变): https://en.cppreference.com/w/cpp/thread/condition_variable

  1. 在用于保护共享变量的同一个互斥锁上获取 std::unique_lockstd::mutex
  2. 要么
    1. 检查条件,以防它已经更新并通知
    2. 执行 wait、wait_for 或 wait_until。等待操作以原子方式释放互斥锁并暂停线程的执行。
    3. 当通知条件变量,超时超时,或者发生虚假唤醒时,线程被唤醒,互斥量被 原子地重新获得。然后线程应该检查条件和 如果唤醒是虚假的,则继续等待。

【讨论】:

  • 正确,所以基本上如果条件为假,线程执行被阻塞/暂停,直到它从其他地方发出信号,在这种情况下是bar,当它被暂停时,互斥锁不再被它收购
  • @xyf 基本上:是的。
  • 谢谢。跟进:在这种情况下,信号量没有意义,因为如果先运行 foo 会导致死锁?
  • @xyf 互斥量是计数为 1 的信号量。所以从技术上讲,您使用的是信号量。计数大于 1 的主要用例是可以并行使用但需要限制并行用户数量的资源(此类资源的示例是一次打开的套接字数量 - 例如,默认情况下它在 1000 左右Linux 在 MacOS 上大约有 200 个)。对于这种情况,您可以使用信号量让 N 个进程并行获取锁,但 N+1 个进程将被阻塞,直到 N 个进程之一释放锁。
  • @xyf 你的程序看起来像一个简单的“生产者-消费者”模式,所以经典的解决方案是你提出的那个或者有两个信号量的那个:见Little book of semaphores。目前我想不出任何仅使用单个信号量的解决方案。
猜你喜欢
  • 1970-01-01
  • 2011-03-03
  • 2012-12-25
  • 2015-06-19
  • 2012-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多