Linux学习_线程信号量
Posted Leslie X徐
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux学习_线程信号量相关的知识,希望对你有一定的参考价值。
线程同步——信号量
信号量
- 概念
- 可以以图书馆借阅和归还为类比,借阅则书目减1,归还则书目加1,若书目为0则已借阅完不可再借阅。
- 信号量从本质上是一个非负整数计数器,是共享资源的数目,通常被用来控制对共享资源的访问。
- 信号量可以实现线程的同步和互斥。
- 通过sem_post()和sem_wait()函数对信号量进行加减操作从而解决线程的同步和互斥。
- 信号量数据类型:
- sem_t
- 信号量的创建和销毁
#include <semaphore.h>
int sem_init(sem_t* sem, int pshared, unsigned value);
int sem_destroy(sem_t* sem);
- 返回:成功返回0,出错返回错误编号
- 参数:
- sem:信号量指针。
- pshared:是否在进程间共享(跨进程共享)的标志,0为不共享,1为共享。
- value:信号量的初始值。
- 信号量的加减操作
#include <semaphore.h>
//功能:增加信号量的值
int sem_post(sem_t* sem);
//功能:减少信号量的值
int sem_wait(sem_t* sem);
//功能:sem_wait()的非阻塞版本
int sem_trywait(sem_t* sem);
- 返回:成功返回0,出错返回错误编号
- 调用sem_post()一次,信号量做加1操作
- 调用sem_wait()一次,信号量做减1操作
- 当线程调用sem_wait()后,若信号量的值小于0则线程阻塞。只有其他线程在调用sem_post()对信号量做加操作后并且其值大于或等于0时,阻塞的线程才能继续运行。
- 案例:
创建a,b,c线程,运行顺序为c,b,a
/*
* sem_test.c
* 创建a,b,c线程,运行顺序为c,b,a
*/
#include <semaphore.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
//定义线程信号量
sem_t sem1; //a和b之间信号量
sem_t sem2; //b和c之间信号量
//定义线程运行函数
void* a_fn(void* arg)
{
sem_wait(&sem1);
printf("thread a running\\n");
return (void*)0;
}
void* b_fn(void* arg)
{
sem_wait(&sem2);
printf("thread b running\\n");
//释放线程a
sem_post(&sem1);
return (void*)0;
}
void* c_fn(void* arg)
{
printf("thread c running\\n");
/*
* 释放线程b(对线程信号量sem2加1操作
* 让阻塞的线程b继续运行)
*/
sem_post(&sem2);
return (void*)0;
}
//主函数
int main(int argc, char **argv)
{
//定义三个线程标识符
pthread_t a, b, c;
//线程信号量的初始化,不共享,初始值为0
sem_init(&sem1, 0, 0);
sem_init(&sem2, 0, 0);
//创建线程
pthread_create(&a, NULL, a_fn, (void*)0);
pthread_create(&b, NULL, b_fn, (void*)0);
pthread_create(&c, NULL, c_fn, (void*)0);
//终止线程
pthread_join(a,NULL);
pthread_join(b,NULL);
pthread_join(c,NULL);
//销毁信号量
sem_destroy(&sem1);
sem_destroy(&sem2);
return 0;
}
输出:
thread c running
thread b running
thread a running
原语:PV操作
- PV操作概念
-
P操作:减一个步长
-
V操作:加一个步长
-
例:
- sem_wait() 减1操作 -> P(1)
- sem_post() 加1操作 -> V(1)
- 利用线程信号量实现线程互斥
银行账户例子:
信号灯I(0或1)
I(1):线程信号量初值为1
线程1取款 | 线程2取款 |
---|---|
P(1) | P(1) |
withdraw() | withdraw() |
V(1) | V(1) |
- 利用线程信号量实现线程同步
t1(计算)----->Result------>t2(获取结果)
I(0):线程信号量初值为0
t1(计算) | t2(获取结果) |
---|---|
- | P(1) |
计算并将结果放置到Result | 从Result中获取结果 |
V(1) | - |
代码:
/* pthread_cal.c
*
* 一个线程负责计算结果,一个线程负责获取结果
*
* */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
//这个结构体为一个共享资源,使用时需要加锁保护
typedef struct
{
int res;
sem_t sem;
}Result;
//线程函数1:计算并将结果放置Result中
void* set_fn(void* arg)
{
int i=1, sum=0;
//计算1~100的累加,得到5050
for(; i<=100; i++) sum += i;
Result* r = (Result*)arg;
//将计算结果放置到Result的res中
r->res = sum;
//V(1)操作
sem_post(&r->sem);
return (void*)0;
}
//线程函数2:获得结果
void* get_fn(void* arg)
{
Result *r = (Result*)arg;
//P(1)操作,若get_fn线程先运行,这里wait会把它挂起
sem_wait(&r->sem);
//获取计算的结果
int res = r->res;
printf("0x%lx get sum is %d\\n", pthread_self(), res);
return (void*)0;
}
//主函数
int main(void)
{
int err;
pthread_t cal, get;
Result r;
sem_init(&r.sem,0,0);
//启动获取结果的线程
err=pthread_create(&get, NULL,get_fn, (void*)&r);
if(err)perror("pthread create error");
//启动计算结果的线程
err=pthread_create(&cal, NULL,set_fn, (void*)&r);
if(err)perror("pthread create error");
pthread_join(cal, NULL);
pthread_join(get, NULL);
sem_destroy(&r.sem);
return 0;
}
输出:
0xb6dbf460 get sum is 5050
以上是关于Linux学习_线程信号量的主要内容,如果未能解决你的问题,请参考以下文章