1. 信号量与条件变量的区别
应用场景: 信号量即可用于进程间同步,也可用于线程间同步; 条件变量只能用于线程间。
用法上: 条件变量需要和互斥锁配合使用,而信号量之间使用就可以,所以信号量更像是条件变量和互斥锁的组合,所以必然的条件变量的使用会比信号量的使用更加灵活。毕竟,后出现的技术比之前的必然要有先进性(能解决特定的需求),才有其存在的意义。
底层实现上:信号量内部以计数方式来判定是否阻塞(有累计数量的功能),计数单次加减1即每次只能唤醒一个进程或线程。而条件变量以“自定义条件”来判定是否阻塞(优点来了),那么它就可以唤醒一个线程或所有线程(notify_all类似广播)。
信号量具体使用:
1、C语言信号量接口参见 #include<semaphore.h>
(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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
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 <semaphore>
#include <iostream>
#include <thread>
#include <semaphore>
#include <unistd.h>
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<m_needProCnt){
cout<<" product production"<<endl;
streams_sem.release();
++num;
}
}
void inner_custom(){
while(1) {
if ( !streams_sem.try_acquire() )
{
cout<<"want? no way"<<endl;
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<std::mutex> 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<T> m_tasks;
std::mutex m_lock;
//CMutexEx m_mutexClr;
};