C 为什么可以';t如果thread1重复获取锁,thread2是否获取锁?

C 为什么可以';t如果thread1重复获取锁,thread2是否获取锁?,c,multithreading,locking,mutex,C,Multithreading,Locking,Mutex,线程1:锁定、睡眠、解锁; 螺纹2:锁定、解锁 但是thread2永远无法获取锁,thread1重复获取锁 我在Ubuntu 16.04(gcc 5.4)下试过很多次 pthread\u mutex\u t mutex; pthread_mutex_init(&mutex,NULL); std::线程t1([&互斥](){ while(true){ pthread_mutex_lock(&mutex); std::cerrpthread\u mutex\u t不能保证在lock()操作上阻塞的线

线程1:锁定、睡眠、解锁; 螺纹2:锁定、解锁

但是thread2永远无法获取锁,thread1重复获取锁

我在Ubuntu 16.04(gcc 5.4)下试过很多次

pthread\u mutex\u t mutex;
pthread_mutex_init(&mutex,NULL);
std::线程t1([&互斥](){
while(true){
pthread_mutex_lock(&mutex);

std::cerr
pthread\u mutex\u t
不能保证在
lock()
操作上阻塞的线程处于先进先出等待队列中。
只要
thread1
解锁互斥锁,它就会进行下一次迭代并再次锁定互斥锁,成功了

对于调度程序来说,阻止
thread1
并唤醒
thread2
比在
unlock()
操作后让
thread1
运行要复杂得多

这个问题涉及:

这个问题有一个名称。它被称为,当任何线程将互斥锁保持较长时间时,它通常会成为一个问题。在您的示例中,两个线程中的每一个都试图始终保持互斥锁锁定。(是的,它们在循环中每次都解锁互斥锁,但在下一条指令中会重新锁定。)

修复饥饿的最佳方法是,不要将互斥锁保持在必要的时间之外。示例中的一个线程在互斥锁被锁定时调用
sleep()
。在任何实际程序中,这几乎总是一个坏主意

另一种修复方法是使用所谓的“公平”互斥体。公平互斥体的所有权总是授予等待时间最长的线程。它的效率低于常规类型的互斥体,但对于某些饥饿问题,它可以很容易地修复。不幸的是,可能没有任何标准方法(或任何方法)在任何给定平台上获得公平的互斥



仅供参考:在您的示例中,线程1从
sleep()
调用中醒来并释放互斥体。一旦释放互斥体,这是两个线程之间的一场竞赛,看下一个线程将获得互斥体。不幸的是,当枪响时,线程2在等待互斥体时被阻塞。操作系统将线程2移动到“运行队列”它必须等待CPU在其上运行。与此同时,线程1已经在运行。线程1再次获取互斥体,然后线程2很快醒来,发现互斥体已锁定,并返回等待状态。

请发布一个完整的示例,其他人可以使用它来尝试复制您的结果。听起来像是正常的饥饿问题。没有看到一些现实中,很难说它为什么会发生。此外,考虑到你使用<代码> STD::线程< /C>和LAMBDAS,为什么不使用标准的C++锁定原语?但是THEADE2不能获得锁的现象也非常奇怪——它真的,真的不是。想想如果<代码>睡眠< /代码>会发生什么。在这里——您希望看到线程1的长时间运行和线程2的长时间运行,而不是交替的线程1线程2线程1线程2线程2,因为线程2只有在线程1释放它的那一刻尝试锁定时才会获得控制,反之亦然——所以一旦线程获得了锁,它就会停止nd重复地重新锁定它。
睡眠
只会使线程2在正确的时间尝试锁定的可能性大大降低。@trentcl:true,但假设调度程序是默认的
SCHED_OTHER
,并且没有手动调整精细度,只有人为的示例实际上不会产生。诚然,这可能是人为的检查ple是因为关键部分中存在休眠,希望这是从来没有人真正做过的事情。如果从thread1中删除休眠,那么两个线程将被更公平地唤醒;即使thread1首先获取锁,thread2也可以稍后获取锁。将版本与休眠进行比较,thread2在这种情况下根本无法获取锁如何解释?@user3015856
sleep()
不是真正的问题。真正的问题是,任何一个线程在释放互斥锁后所做的下一件事是,它再次锁定互斥锁。这使另一个线程没有真正的竞争机会,因为获取不公平的互斥锁并不是偶然发生的:线程本身必须执行指令才能实现。但是“饥饿”的此时线程没有运行,而刚刚释放互斥锁的线程已经在运行,因此刚刚释放互斥锁的线程总是会赢。感谢您的解释。但它仍然无法解释,如果从thread1中删除睡眠,为什么thread1不能在thread1一次赢后继续赢(正如您所说,线程1将在释放锁后立即再次锁定)?@user3015856线程2在线程1运行时被阻塞。“阻塞”这意味着操作系统必须做真正的工作才能使其再次运行。当线程1释放锁时,操作系统不会立即执行该工作。它所做的只是记下一个事实,即线程2现在是“可运行的”同时,线程1继续运行。线程1接下来要做的就是再次锁定锁。最终,操作系统完成了唤醒线程2的任务,但当它醒来时,它发现锁再次被锁定。此时除了返回等待,它无法做任何其他事。如果从线程1中移除睡眠,结果可能是这样的:螺纹1(重复20次)、螺纹2(重复10次)、螺纹1(重复15次),…但是在thread1中使用sleep,结果是thread2永远不会获得锁,这在我看来是非常奇怪的。也许@trentcl的评论很有用:睡眠只会使线程2在正确的时间尝试锁的可能性大大降低。感谢您指出我们经常期望它像FIFO一样的错误,以及FI的实现FO互斥也是非常有趣的。
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

std::thread t1([&mutex]() {
    while (true) {
        pthread_mutex_lock(&mutex);
        std::cerr << "thread 1" << std::endl;
        sleep(1);
        pthread_mutex_unlock(&mutex);
    }
});

std::thread t2([&mutex]() {
    while (true) {
        pthread_mutex_lock(&mutex);
        std::cerr << "thread 2" << std::endl;
        pthread_mutex_unlock(&mutex);
    }
});

t1.join();
t2.join();