我正在开发一个多线程程序,其中一个循环流线程阻塞/休眠/等待来自控制线程的指示以在其循环中运行,然后可以在用户禁用所有流时“暂停”。
通常我会使用条件变量来处理类似的问题,但是由于问题的性质(任何被请求的流都应该允许循环开始,并且只有当最后一个流被请求禁用时才应该停止),我认为解决方案会比我想要的更复杂(不是很复杂,但至少需要更多的簿记)。我想看看在reading about them here之后,信号量是否是一个好的选择:
信号量也经常用于信令/通知的语义,而不是互斥,通过用0初始化信号量,从而阻止尝试获取()的接收器,直到通知器通过调用release(n)“发信号”。在这方面,信号量可以被认为是std::condition_variables的替代品,通常具有更好的性能。
我模拟了一个简单的例子,因为这是我第一次使用C++20 std::semaphore
s,但这个例子并没有像预期的那样工作:
#include <iostream>
#include <thread>
#include <semaphore>
#include <unistd.h>
std::counting_semaphore streams_sem{0};
void stream_thread(){
while (1){
streams_sem.acquire();
std::cout << "Sending images!\n";
usleep(500000);
streams_sem.release();
}
}
int main(int argc, char const* argv[]){
std::thread t1(stream_thread);
std::cout << "Main thread starting streams\n";
streams_sem.release();
usleep(3e6);
std::cout << "Main thread stopping streams\n";
streams_sem.acquire();
return 0;
}
当运行时,这是我得到的输出:
Main thread starting streams
Sending images!
Sending images!
Sending images!
Sending images!
Sending images!
Sending images!
Main thread stopping streams
Sending images!
Sending images!
... and on
我可以假设主线程在循环release()
和acquire()
之间的时间内没有被调度,尽管我希望它最终会捕获它(以及时的方式,最多几秒),因为主线程已经在acquire()
上被阻塞,并且因为上面与条件变量的比较。
这就引出了我的主要(子)问题:
1.如果在循环线程中的release()
之后有一个短暂的(10- 100 uS)睡眠,我会遇到什么样的陷阱(除了浪费时间)?IE可能仍然有可能无法阻止它,使它成为一个坏的解决方案?
1.这是否是对信号量的不适当或无效使用?或者我可以做些什么,让它们更像一个内置计数器的信号/条件变量?
我也愿意接受任何其他可以实现此功能的方法。我尽量避免使用条件变量,因为我相信解决方案会很复杂,但如果有另一种同步方法似乎更适合,我也很想听听。
1条答案
按热度按时间gopyfrb31#
我可以假设主线程在循环release()和acquire()之间的时间内不会被调度,尽管我希望它最终会捕获它(及时地,最多几秒),因为主线程已经在acquire()上阻塞,并且因为上面与条件变量的比较
在release和re-acquire之间只有一条指令,这是一个无条件跳转。你对主线进入的可能性非常乐观。
据我所知,你的逻辑应该是这样的:
1.信号量计数器表示活动流的数量
1.当该数字不为零时,线程应继续处理流
1.另一个线程负责启动/停止流(递增/递减计数器)
这里没有任何内容建议工作线程应该 * 永远 * 修改流的数量。所以信号量至少不是一个完美的选择。
如果我们接受线程正在使用信号量来检查非零计数器,那么它应该在完成工作之前获取并立即释放。
这里省略了所有关于这些流是什么以及如何与主线程协调对它们的访问的讨论。这是通常由与条件变量相关联的互斥体保护的内容。
也就是说,我不相信这个例子显示了所有需要同步的共享状态,如果无论如何都要添加一个互斥锁,那么信号量最终不会比常规条件变量更简单。