为方便操作系统进程同步与互斥内容的学习,写了一个简易的semaphore类。
#pragma once
#include <mutex>
#include <sstream>
class Semaphore { //信号量类
int _count; //信号量初始值, 为1等价于mutex, 为0可实现同步, 大于零可实现多资源的同步
std::string _name; //信号量的名字,便于打印调试
std::mutex _mutex;
std::condition_variable _condition;
public:
Semaphore(int value, std::string name = "null") : _count(value), _name(name) {}
friend void P(Semaphore& S) { //wait操作
std::unique_lock<std::mutex> lock(S._mutex);
S._count--;
if (S._count < 0) {
S._condition.wait(lock);
}
}
friend void V(Semaphore& S) { //signal操作
std::lock_guard<std::mutex> lock(S._mutex);
S._count++;
if (S._count <= 0) {
S._condition.notify_one();
}
}
std::string inf() { //返回一个含有信号量信息的字符串,调试用
std::stringstream ss;
ss << _name << ":" << _count << ' ';
return ss.str();
}
};
使用方法:
创建信号量:
Semaphore S(1); //括号里面第一个参数是信号量初始值,第二是信号量名(可以不写)
Semaphore S(0, "mutex");
可像操作系统书上一样创建了信号量后直接P、V操作,更加接近教材。
P(S);
V(S);
调用inf()方法可返回一个信号量信息的字符串,用来出错调试
cout << S.inf();
而有时想让子线程用detach方式与父线程分离,但是又想父线程在所有子线程执行完毕后再退出。如果用上面的信号量也能实现,假设一个父线程要等待n个子线程执行完毕后才能结束,令初始信号量为0,在每一个子线程结束前做一个V(S)操作,在父进程结束前做n个P(S)操作即可。但这样会频繁的进行唤醒操作,代码写起来也比较繁琐。下面我写了一个专门用来实现等待所有子线程都完成的信号量:
#include <mutex>
#include <sstream>
class Semaphore_finish { //所有任务完成信号量
int _count; //要完成任务总数
std::string _name; //信号量名称
std::mutex _mutex;
std::condition_variable _condition;
public:
Semaphore_finish(int value, std::string name = "null") : _count(value), _name(name) {}
friend void WaitFinish(Semaphore_finish& S) {
//若所有任务没有完成该函数将被阻塞直到所有任务完成
std::unique_lock<std::mutex> lock(S._mutex);
if (S._count > 0) {
S._condition.wait(lock);
}
}
friend void Release(Semaphore_finish& S) {
//完成一个任务就释放掉一个count,当所有任务完成时就解锁所有等待所有任务完成的线程
std::lock_guard<std::mutex> lock(S._mutex);
S._count--;
if (S._count == 0) {
S._condition.notify_all();
}
}
std::string inf() {
std::stringstream ss;
ss << _name << ":" << _count << ' ';
return ss.str();
}
};
使用时,若有n个等待完成的子线程,就创建对象Semaphore_finish S(n);,只需在每个子线程最后加一个Release(S),在父线程最后加一个WaitFinish(S)就可达到目标,并且只会在所有子线程完成后才进行一次唤醒,效率更高。