应用场景: 信号量即可用于进程间同步,也可用于线程间同步; 条件变量只能用于线程间。
用法上: 条件变量需要和互斥锁配合使用,而信号量之间使用就可以,所以信号量更像是条件变量和互斥锁的组合,所以必然的条件变量的使用会比信号量的使用更加灵活。毕竟,后出现的技术比之前的必然要有先进性(能解决特定的需求),才有其存在的意义。
底层实现上:信号量内部以计数方式来判定是否阻塞(有累计数量的功能),计数单次加减1即每次只能唤醒一个进程或线程。而条件变量以“自定义条件”来判定是否阻塞(优点来了),那么它就可以唤醒一个线程或所有线程(notify_all类似广播)。
信号量具体使用:
1、C语言信号量接口参见 #include
(1) 信号量的创建接口,int sem_init(sem_t *sem,int pshared,unsigned int value);
调用成功时返回0,失败返回-1.
参数一: 信号对象指针
参数二: 设置它的进程间共享选项,0为不共享,其余则共享;
参数三:设置信号量初始值,一般填0,即开始默认阻塞(当信号量值小于、等于0则阻塞)。
(2) 信号量的等待接口,int sem_wait(sem_t *sem);
如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0或小于0,则线程阻塞。 调用成功时返回0,失败返回-1.
(3)信号量值加1接口,int sem_post(sem_t *sem);
释放信号量,让信号量的值加1。 调用成功时返回0,失败返回-1.
(4)信号量的清理接口(相对sem_init而言),int sem_destroy(sem_t *sem);
成功时返回0,失败时返回-1.
示例:
- #include
- #include
- #include
- #include
- #include
-
- #include
-
- sem_t g_sem;///
-
- void func1(void* arg)
- {
- sem_wait(&sem); /线程1初始阻塞
- int *running=arg;
- printf("thread running1\n");
- printf("%d\n",*running);
- }
-
- void func2(void* arg)
- {
- printf("pthread2 running\n");
- sem_post(&sem); //线程2增加信号量,致使线程1阻塞被唤醒以执行后续代码
- }
-
- int main()
- {
- sem_init(&g_sem,0,0);信号量初始化为本进程内多线程使用,初始阻塞
- pthread_t thread[2];
- int a = 1;
- pthread_create(&(thread[0]),NULL,(void*)func1,(void*)&a);
- sleep(3);
- pthread_create(&(thread[1]),NULL,(void*)func2,(void*)&a);
- pthread_join(thread[0],NULL);
- pthread_join(thread[1],NULL);
- sem_destroy(&sem); //信号量销毁
-
- return 0;
- }
2、C++ --std=c++20 信号量 counting_semaphore ,参见 #include
- #include
- #include
- #include
- #include
-
- using namespace std;
-
- std::counting_semaphore streams_sem{0};
-
- void custom_thread(){
- while (1){
- streams_sem.acquire();
- std::cout << "shopping!\n";
- usleep(50000);
- }
- }
-
- class Prod{
- public:
- Prod(int nCnt){ m_needProCnt = nCnt; }
- void product() {
- int num = 0;
- while(num
- cout<<" product production"<
- streams_sem.release();
- ++num;
- }
-
- }
- void inner_custom(){
- while(1) {
- if ( !streams_sem.try_acquire() )
- {
- cout<<"want? no way"<
- break;
- }
- std::cout << "want inner shopping!\n";
- usleep(100000);
- }
- }
- private:
- int m_needProCnt;
- };
-
- int main(int argc, char const* argv[]){
- std::thread t1(custom_thread);
- t1.detach();
- Prod prod(20);
- std::thread t2(&Prod::product, &prod);
- std::thread t3(&Prod::inner_custom, &prod);
- t2.join();
- t3.join();
-
- return 0;
- }
条件变量具体使用:
- template <typename T>
- class BlockQue {
- public:
- BlockQue():m_end(false){}
- ~BlockQue(){
- endBlockQue();
- }
- void addTask(T data){
- m_tasks.push(data);
- m_task_cv.notify_all();
- }
- void clearTask(bool del){
- //CAutoLock lock(m_mutexClr);
- while(!m_tasks.empty()){
- T data = std::move(m_tasks.front());
- m_tasks.pop();
- if(del){
- delete data;
- }
- }
- }
- bool isEmpty(){
- return m_tasks.empty();
- }
- int size(){
- return m_tasks.size();
- }
- int getTask(T& data){
- //CAutoLock lock(m_mutexClr);
- std::unique_lock
lock{ m_lock }; - m_task_cv.wait(lock, [this]{
- return m_end.load() || !m_tasks.empty();//true则不阻塞
- });
- if (m_tasks.empty()){
- return -1;
- }
- data = std::move(m_tasks.front());
- m_tasks.pop();
- return 0;
- }
-
- void initBlockQue() {
- m_end.store(false);
- }
- void endBlockQue(){
- if (!m_end.load()){
- m_end.store(true);
- m_task_cv.notify_all();
- }
- }
- private:
- std::atomic<bool> m_end;
- std::condition_variable m_task_cv;
- std::queue
m_tasks; - std::mutex m_lock;
- //CMutexEx m_mutexClr;
- };
-
相关阅读:
私有git仓库只支持http情况下go mod tidy 和 go get 默认走https的问题处理 GOINSECURE
代码随想录1刷—二叉树篇(三)
微服务 Spring Boot 整合Redis分布式锁 Lua脚本 实现优惠卷秒杀 一人一单
CogVLM & CogAgent模型部署
微软开发新模型;YouTube 推出新AI功能;可折叠iPhone 或发布?
【Java集合类面试三十】、BlockingQueue中有哪些方法,为什么这样设计?
企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
2023年 python结合excel实现快速画图(零基础快速入门)
数字时代古文的传承———云南文化瑰宝“爨文化“(我为家乡发声)
java通过zookeeper 高可用方式连接hiveserver2
-
原文地址:https://blog.csdn.net/fengdijiang/article/details/132871790