为什么此互斥代码无法按预期工作
Why does this mutex code not work as expected?
关于这个主题的帖子和答案一堆乱七八糟,但似乎没有一个能完全模拟我的问题。在谷歌搜索和搜索堆栈溢出之后,我不太明白我问题的答案。
我有两个线程,一个主线程和一个从线程。从站需要等待主服务器才能超过某个点,所以我创建了一个互斥锁作为全局变量:
pthread_mutex_t lock;
然后在主线程的初始化中,早在从线程有机会访问它之前,我就锁定了它:
在主线程中初始化:
pthread_mutex_lock(&lock)
然后在奴隶中,当需要等待主人时,我这样做:
奴隶必须在这里等待:
pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);
同时,回到主人,我有这个时间"释放"被阻止等待的奴隶:
pthread_mutex_unlock(&lock);
pthread_mutex_lock(&lock);
(注意:锁定/解锁的顺序与主节点相反。
根据我对互斥锁的理解,我认为奴隶会撞到锁上,然后卡在那里等待主人解锁它,然后立即再次锁定它。就时间而言,从站需要很长时间(保证)才能再次回到这里,因此主站将有很多时间重新锁定它。同样,主人在一段时间内不会再回到这里,我们不需要担心主人或奴隶将另一个打回这些检查站。
当它没有按预期工作时,我扔了一些 printf 来确认主设备解锁,然后在从站解锁之前重新锁定互斥锁。我的理解是,从站早在主站到达那里进行解锁和(重新)锁定之前就已经请求了锁,无论主站解锁和(重新)锁定之间的时间有多短,从站仍然应该能够锁定互斥锁,因为他已经"排队"等待。
但是,我看到的情况是,主设备解锁互斥锁,然后立即重新锁定它,即使从站已被耐心阻止等待锁定它的机会。
以下是包含 printf 的代码和生成的输出:
奴隶:
printf("slave thread starting lock sequencen");fflush(stdout);
pthread_mutex_lock(&lock);
printf("slave thread intra lock sequencen");fflush(stdout);
pthread_mutex_unlock(&lock);
printf("slave thread completed lock sequencen");fflush(stdout);
主人:
printf("master thread starting lock sequencen");fflush(stdout);
pthread_mutex_unlock(&lock);
printf("master thread intra lock sequencen");fflush(stdout);
pthread_mutex_lock(&lock);
printf("master thread completed lock sequencen");fflush(stdout);
现在这是我看到的输出:
从线程起始锁定序列
。然后一段时间过去(几秒钟),当从站被阻止时,最后出现:
主线程启动锁定顺序
主线程内锁序列
主线程完成锁定序列
与此同时,奴隶没有进一步的进展,他永远被封锁。我本以为他会阻止主人重新锁定,应该吐出他的印记,表明他已经向前移动了。此输出清楚地表明,被阻止的从站没有机会锁定互斥锁,即使他正在耐心地排队等待轮到他。
那么我对互斥体和锁定/解锁缺少什么?
-燃气轮机-
正如您问题的评论中所述,pthreads 互斥体不能保证公平性。
此作业的正确工具是共享标志变量,受互斥锁保护并使用条件变量等待:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int flag = 0;
从属等待旗帜:
pthread_mutex_lock(&lock);
while (!flag)
pthread_cond_wait(&cond, &lock);
pthread_mutex_unlock(&lock);
当主站想要释放从站时,它会设置标志并发出条件变量的信号:
pthread_mutex_lock(&lock);
flag = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);
(请注意,在pthread_cond_wait()
内部阻止时,互斥锁不会保持)。
当它没有按预期工作时,我扔了一些 printf 来确认主设备解锁,然后在从站解锁之前重新锁定互斥锁。我的理解是,从站早在主站到达那里进行解锁和(重新)锁定之前就已经请求了锁,无论主站解锁和(重新)锁定之间的时间有多短,从站仍然应该能够锁定互斥锁,因为他已经"排队"等待。
没有理由对线程公平,如果你虐待他们,他们不会提出工会申诉。但是有理由让整个系统尽快完成尽可能多的工作。停止一个线程以启动另一个线程会对性能产生重大负面影响,因为新线程启动时所有缓存都处于冷状态,并且当您切换回来时也会发生同样的事情。
你的工作是确保你的代码在执行时完成你想要它做的工作。调度程序将尝试尽快完成工作,只是偶尔点头以求公平。
也许您可以在信号量之后对问题进行建模,我发现这通常更容易理解和实现。 类似于以下伪代码示例。
//before create slave thread, create semaphore first
sem_t data_ready, slave_done;
sem_init(&data_ready, 0);
sem_init(&slave_done, 0);
在主线程中:
//master do something AAA
sem_post(data_ready);
//master do something BBB
sem_wait(slave_done); //master may sleep here, and maybe interrupted by signal, need to handle that
//master do something CCC
在从属线程中:
//slave do something DDD
sem_wait(&data_ready); //slave may sleep here, and maybe interrupted by signal, need to handle that
//slave do something EEE
sem_post(&slave_done);
//slave do something FFF
您可能会发现执行顺序应该是 AAA [BBB/DDD] EEE [FFF/CCC],它确保主>AAA 在从>EEE 之前,后者在主>CCC 之前运行。
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 当我从下面的代码中删除关键字 virtual 时,它可以正常工作,否则会出现错误。在这里"virtual"字的意义是什么?
- C++代码在台式机上工作正常,但在笔记本电脑上则不行
- 我想使用此代码单击某个特定窗口,但它无法正常工作
- C++代码停止工作错误使用cout内部函数
- 我的代码无法正常工作。谁能指出错误?
- 有人可以详细解释这个回文代码是如何工作的吗?
- 代码在 CodeSignal 中工作不正确。不确定这是否是我的代码缺陷
- 代码在Visual Studio 2017中不起作用,但在VS代码中工作
- 以下代码如何工作以每次为唯一调用堆栈唯一实例化模板函数?
- 添加新行时工作代码引发异常.调试技巧?
- 一个非常简单的win32套接字代码,但工作错误
- 需要WXLISTCTRL的代码更改为虚拟样式WXListCtrl的工作代码
- C++ / CannyEdgeDetection.exe 已停止工作 代码块 /OpenCV 错误:断言失败
- C //尝试重写一个工作代码以包括我的功能
- 如何在不保存文件的情况下制作打印屏幕并将其发送到FTP服务器?我的工作代码将文件保存到HDD
- 如何在不破坏当前工作代码的情况下添加小数
- 无法理解工作代码和损坏代码之间的区别
- .exe已停止工作代码块 CPP
- rapidjson:用于从文件中读取文档的工作代码