【线程同步1】使用信号量实现线程同步
在之前的博文中有讨论过如何使用信号量实现进程同步(【进程同步】使用信号量实现进程同步(附C++实现代码)),该文中详解了同步的概念以及信号量相关函数的具体使用和实验举例,今天我们来学习如何使用信号量实现线程同步。
信号量
信号量是一个与队列有关的整形变量,可以将它想象为一个数后面拖着一条排队的队列。如图所示:
其中信号量值代表:
- 该值大于0,当前有n个可用资源;
- 该值等于0,当前可用资源为0;
- 该值小于0,此时有n个进程在等待资源
信号量实现伪代码:
struct semaphone {
int count;
queueType queue;
};
void semWait(semaphore s){
s.count--;
if(s.count < 0){
// 进程排队
// 中断这一进程
}
}
void semSignal(semaphore s){
s.count++;
if(s.count <= 0){
// 将队列中第一个进程移除队列
// 该进程为就绪状态
}
}
其中的semWait可以理解为申请资源,P操作
其中的semSignal为理解为资源,V操作
当固定了可共享资源的数目之后,每当某个线程使用该资源,就执行P操作,若信号量大于0,则信号量数目减一且该线程继续执行;否则,该信号量数目减一且将该线程放入等待资源队列中,停止运行该线程。
具体关于信号量的函数在进程使用信号量实现同步的文章中已经详解过了,故而本文只摘取关于信号量的函数定义总结,具体关于该信号量函数的使用详解,可以翻看我在本博文开头提到的(【进程同步】使用信号量实现进程同步(附C++实现代码))中进行查看。
信号量函数总结
命名信号量相关函数
操作 | 函数定义 |
---|---|
创建 | sem_t *sem_open(const char *name, int oflag, mode_t mode,unsigned int value) |
删除 | int sem_unlink(const char *name) |
打开 | sem_t *sem_open(const char *name, int oflag) |
关闭 | int sem_close(sem_t *sem) |
挂出 | int sem_post(sem_t *sem) |
等待 | int sem_wait(sem_t *sem) |
尝试等待 | int sem_trywait(sem_t *sem) |
获取信号量的值 | int sem_getvalue(sem_t *sem, int *sval) |
匿名信号量相关函数
匿名信号量可以用于同一个进程中的线程使用。
操作 | 函数 |
---|---|
初始化 | int sem_init (sem_t *sem , int pshared, unsigned int value) |
销毁 | int sem_destroy(sem_t *sem) |
挂出 | int sem_post(sem_t *sem) |
等待 | int sem_wait(sem_t *sem) |
尝试等待 | int sem_trywait(sem_t *sem) |
获取信号量的值 | int sem_getvalue(sem_t *sem, int *sval) |
使用信号量实现进程同步
运行代码如下:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
using namespace std;
void* func(void* arg){
for(int i=0; i<10; ++i){
usleep(100);
cout << pthread_self() << ":before" << endl;
usleep(100);
cout << pthread_self() << ":exit" << endl;
}
pthread_exit(new int(2));
}
int main(){
pthread_t tid;
pthread_create(&tid, NULL, func, NULL);
usleep(500); // 等待子进程跑起来
func(NULL);
pthread_join(tid, NULL);
}
运行结果如下:
会发现线程运行顺序是混乱的,我们增添信号量,使得两个线程按照一定顺序执行。
运行代码如下:
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
using namespace std;
void* func(void* arg){
sem_t* p = (sem_t*)arg;
for(int i=0; i<10; ++i){
usleep(100);
sem_wait(p);
cout << pthread_self() << ":before" << endl;
usleep(100);
cout << pthread_self() << ":exit" << endl;
sem_post(p);
}
}
int main(){
sem_t sem; // 对线程而言是共享的,所以这个信号量可以不是用共享内存定
义
sem_init(&sem, 0, 1);
pthread_t tid;
pthread_create(&tid, NULL, func, &sem);
usleep(500); // 等待子进程跑起来
func(&sem);
pthread_join(tid, NULL);
sem_destroy(&sem);
}
执行部分结果如下:
两个线程执行顺序显然是有序的,只有一个线程可以进入func函数的关键代码(sem_wait至sem_post部分),从而实现了线程同步。
版权声明:本文为weixin_50941083原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。