linux下信号量(semaphore)的使用例程
semaphore是系统中的东西,所以不同系统中包含头文件不同,在linux中包含
<>
说明:int sem_wait(sem_t *sem)
: 等待信号量>0,信号量为0表示无可用资源来使用,就要一直等下去,当信号量>0时,表示有空余资源可用,那么就占用一个资源给当前线程用,并将信号量减一.int sem_post(sem_t *sem)
: 释放资源,将信号量加1,表示有一个资源空闲.
视频教程详细讲解
课件笔记
例程一:
程序说明:
完成一个多线程demo, 保证三个线程执行顺序, 即顺序打印a,b,c.
#include <iostream>
#include <>
#include <memory>
#include <thread>
using namespace std;
sem_t firstJobDone; // c++ 线程如果执行成员函数会对this下的成员做一份拷贝,所以信号量不能作为 Foo 的成员变量。
sem_t secondJobDone;
class Foo : public std::enable_shared_from_this<Foo> { // std::enable_shared_from_this 能让一个对象(假设其名为 t ,\
// 且已被一个 std::shared_ptr 对象 pt 管理)安全地生成其他额外的 std::shared_ptr 实例(假设名为 pt1, pt2, ... ) ,它们与 pt 共享对象 t 的所有权。
/* 使用场合:
当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr。
参考: /caoshangpa/article/details/79392878 */
public:
int v;
public:
Foo() {
sem_init(&firstJobDone, 1, 0); // int sem_init(sem_t *sem, int pshared, unsigned int value);
// Param : pshared
/* 1. The pshared argument indicates whether this semaphore is to be
shared between the threads of a process, or between processes.
2. If pshared has the value 0, then the semaphore is shared between the
threads of a process, and should be located at some address that is
visible to all threads (., a global variable, or a variable allo‐
cated dynamically on the heap).
3. If pshared is nonzero, then the semaphore is shared between processes,
and should be located in a region of shared memory (see shm_open(3),
mmap(2), and shmget(2)). (Since a child created by fork(2) inherits
its parent's memory mappings, it can also access the semaphore.) Any
process that can access the shared memory region can operate on the
semaphore using sem_post(3), sem_wait(3), and so on.*/
sem_init(&secondJobDone, 1, 0);
}
void first() {
cout << 'a' << endl;
sem_post(&firstJobDone); // 释放资源,将信号量加1,表示有一个资源空闲
//sem_getvalue(&firstJobDone,&v);
//cout<<"1done:"<<v<<"addr:"<<&firstJobDone<<endl;
}
void second() {
//sleep(2);
//sem_getvalue(&firstJobDone,&v);
//cout<<"11done:"<<v<<"addr:"<<&firstJobDone<<endl;
sem_wait(&firstJobDone); // 等待信号量>0,信号量为0表示无可用资源来使用,就要一直等下去,当信号量>0时,表示有空余资源可用,那么就占用一个资源给当前线程用,并将信号量减一.
cout << 'b' << endl;
sem_post(&secondJobDone);
}
void third() {
sem_wait(&secondJobDone);
cout << 'c' << endl;
}
std::shared_ptr<Foo> get_this() {
return shared_from_this();
}
};
int main()
{
std::shared_ptr<Foo> foo_ptr = make_shared<Foo>();
// std::thread t{[]{cout<<"xxx";}};
std::thread t1{ &Foo::first, *foo_ptr }; // 这里 &Foo::first 是取类Foo的成员函数first的地址,其实就相当于把该函数传入,
// 然后第二个参数是传入函数first的参数, 类成员函数的第一个参数是this指针
std::thread t2{ &Foo::second, *foo_ptr };
std::thread t3{ &Foo::third, *foo_ptr }; // c++ 获取成员函数指针的指定写法,并且需要传入this指针,因为成员函数第一个参数是this
// std::thread t1{foo_ptr->first,*foo_ptr}; // error
// std::thread t1{&(foo_ptr->first),*foo_ptr}; // error
t1.join();
t2.join();
t3.join();
return 0;
}
命令行编译:
注意:
- 这里用到了c++11的标准库,所以要加
-std=c++11
- 这里用到了多线程,所以要加
-pthread
g++ -o semaphore_test semaphore_test.cpp -std=c++11 -pthread
./semaphore_test
例程二:
互斥量的使用可以用 mutex 库,lock/unlock,lock_guard(自动解锁,不可以手动lock/unlock),unique_lock(可以手动lock/unlock)
给出 leetcode 1195 的解法:
题目描述:
编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:
- 如果这个数字可以被 3 整除,输出 “fizz”。
- 如果这个数字可以被 5 整除,输出 “buzz”。
- 如果这个数字可以同时被 3 和 5 整除,输出 “fizzbuzz”。
例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。
假设有这么一个类:
class FizzBuzz {
public FizzBuzz(int n) { ... } // constructor
public void fizz(printFizz) { ... } // only output "fizz"
public void buzz(printBuzz) { ... } // only output "buzz"
public void fizzbuzz(printFizzBuzz) { ... } // only output "fizzbuzz"
public void number(printNumber) { ... } // only output the numbers
}
请你实现一个有四个线程的多线程版 FizzBuzz, 同一个 FizzBuzz 实例会被如下四个线程使用:
- 线程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。
- 线程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。
- 线程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。
- 线程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。
LeetCode链接
感觉类似于状态机,通过对相应状态的信号量进行置位(加1),从而实现向下一状态的转换.
代码:
#include <>
#include <functional>
#include <thread>
using namespace std;
class FizzBuzz {
private:
int n;
int cur;
sem_t sem_fizz;
sem_t sem_buzz;
sem_t sem_fizz_buzz; //这三个信号量分别用于线程的同步
sem_t sem_num; //用于触发数字增加
public:
FizzBuzz(int n) {
this->n = n;
cur = 0;
sem_init(&sem_fizz, 0, 0);
sem_init(&sem_buzz, 0, 0);
sem_init(&sem_fizz_buzz, 0, 0); //这三者的信号量初始化都置位为0,三个线程均被阻塞
sem_init(&sem_num, 0, 1); //初始化信号量为1,触发开始
}
// printFizz() outputs "fizz".
void fizz(function<void()> printFizz) {
while(cur <= n){
sem_wait(&sem_fizz); //等待信号量为1
if(cur > n) break;
printFizz();
sem_post(&sem_num); //将信号量sem_num加1,变为1
}
}
// printBuzz() outputs "buzz".
void buzz(function<void()> printBuzz) {
while(cur <= n){
sem_wait(&sem_buzz);
if(cur > n) break;
printBuzz();
sem_post(&sem_num);
}
}
// printFizzBuzz() outputs "fizzbuzz".
void fizzbuzz(function<void()> printFizzBuzz) {
while(cur <= n){
sem_wait(&sem_fizz_buzz);
if(cur > n) break;
printFizzBuzz();
sem_post(&sem_num);
}
}
// printNumber(x) outputs "x", where x is an integer.
void number(function<void(int)> printNumber) {
while(++cur <= n){
sem_wait(&sem_num); // 等待信号量sem_num变为1
if(cur % 3 == 0 && cur % 5 == 0){
sem_post(&sem_fizz_buzz); // 将sem_fizz_buzz置为1
}else if(cur % 3 == 0){
sem_post(&sem_fizz); // 将sem_fizz置为1
}else if(cur % 5 == 0){
sem_post(&sem_buzz); // 将sem_buzz置为1
}else{
printNumber(cur);
sem_post(&sem_num); // 将sem_num置为1
}
}
// 以下三个post通过更新sem_fizz等信号量,调动其他线程运行,进而结束所有线程
sem_post(&sem_fizz);
sem_post(&sem_buzz);
sem_post(&sem_fizz_buzz);
}
};
int main(int argc, char** argv){
FizzBuzz fizzBuzz(15);
std::function<void()> printFizz = [](){printf(" fizz ");};
std::function<void()> printBuzz = [](){printf(" buzz ");};
std::function<void()> printFizzBuzz = [](){printf(" fizzbuzz ");};
std::function<void(int)> printNum = [](int x){printf(" %d ", x);};
std::thread th[4];
th[0] = std::thread(&FizzBuzz::fizz, &fizzBuzz, printFizz);
th[1] = std::thread(&FizzBuzz::buzz, &fizzBuzz, printBuzz);
th[2] = std::thread(&FizzBuzz::fizzbuzz, &fizzBuzz, printFizzBuzz);
th[3] = std::thread(&FizzBuzz::number, &fizzBuzz, printNum);
for(auto &ts : th) {
if(ts.joinable()) ts.join();
}
return 0;
}