对于多线程程序来说,同步是指在一定的时间内只允许某一个线程访问某个资源 。 而在
此时间内,不允许其他的线程访问该资源。同步资源的方式:互斥锁、条件变量、读写锁、
信号量。
信号量和互斥锁的区别: 互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区
信号量。
一、基本原理
信号量和互斥锁的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。
以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
二、API
1、创建信号量
int sem_init(sem_t *sem , int pshared , unsigned int value) ;
sem :指向信号量对象
pshared : 指明信号量的类型。不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享。
value : 指定信号量值的大小
调用成功返回0
2、sem wait 函数 。
该函数用于以原子操作的方式将信号量的值减 1 。 原子操作就是 ,如果两个线程企图 同
时给一个信号量加 1 或减 l ,它们之间不会互相干扰 , 函数的原型如下:
int sem_wait(sem_t *sem);
调用成功返回0,调用成功返回-1
3、 sem_post 函数 。
该函数用于以原子操作的方式将信号量的值加 1 ,函数的原型如下:
int sem__post (sem_ t *sem);
调用成功返回0,调用成功返回-1
4、sem_destroy 函数
该函数用于对用完的信号量进行清理,函数的原型如下:
int sem_destroy ( sem_t *s em);
成功时返回 0 ,失败时返回- 1 。
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define CUSTOMER_NUM 10
/* @Scene: 某行业营业厅同时只能服务两个顾客。
* 有多个顾客到来,每个顾客如果发现服务窗口已满,就等待,
* 如果有可用的服务窗口,就接受服务。 */
/* 将信号量定义为全局变量,方便多个线程共享 */
sem_t sem;
/* 每个线程要运行的例程 */
void * get_service(void *thread_id)
{
/* 注意:立即保存thread_id的值,因为thread_id是对主线程中循环变量i的引用,它可能马上被修改*/
int customer_id = *((int *)thread_id);
if(sem_wait(&sem) == 0)
{
usleep(100); /* service time: 100ms */
printf("customer %d receive service ...\n", customer_id);
sem_post(&sem);
}
pthread_exit((void*)0);
}
int main(int argc, char *argv[])
{
/*初始化信号量,初始值为2,表示有两个顾客可以同时接收服务 */
sem_init(&sem,0,2);
/*为每个顾客定义一个线程id*/
pthread_t customers[CUSTOMER_NUM];
int i, iRet;
/* 为每个顾客生成一个线程 */
for(i = 0; i < CUSTOMER_NUM; i++)
{
int customer_id = i;
iRet = pthread_create(&customers[i], NULL, get_service, &customer_id);
if(iRet){
perror("pthread_create");
return iRet;
}
else{
printf("Customer %d arrived.\n", i);
}
usleep(10);
}
/* 等待所有顾客的线程结束 */
/* 注意:这地方不能再用i做循环变量,因为可能线程中正在访问i的值 */
int j;
for(j = 0; j < CUSTOMER_NUM; j++)
{
pthread_join(customers[j], NULL);
}
/*销毁信号量*/
sem_destroy(&sem);
return 0;
}
//运行环境Ununtu
把文件命名为Thread.cpp
编译:g++ -o Thread Thread.cpp -lpthread
运行./Thread