linux线程同步方式3——信号量(semaphore)

信号量

  • 信号量
    • 1、定义
    • 2、基本操作
      • 1、信号量初始化sem_init
        • 函数原型
      • 2、信号量等待sem_wait
        • 函数原型
      • 3、发送(释放)信号量sem_post
        • 函数原型
      • 4、测试信号量sem_getvalue
        • 函数原型
      • 5、销毁信号量 sem_destroy
        • 函数原型
  • 应用实例
  • 参考

信号量

1、定义

#include 

信号量又称为信号灯,它是用来协调不同线程(进程)间的数据对象的。本质上,信号量是一个计数器,它用来记录对某个资源的存取状况。一般说来,为了获得共享资源,进程需要执行下列操作:

  • (1) 测试控制该资源的信号量。
  • (2) 若此信号量的值为正,则允许进行使用该资源。线程将信号量减1。
  • (3) 若此信号量为0,则该资源目前不可用,线程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1)。
  • (4) 当线程不再使用一个信号量控制的资源时,信号量值加1。如果此时有线程正在睡眠等待此信号量,则唤醒此线程。

2、基本操作

  • 信号量初始化
  • 等待信号量
    • 当信号量值为 0 时,程序等待;当信号量值大于 0 时,信号量减 1,程序继续运行。
  • 发送信号量
    • 将信号量值加 1
  • 销毁信号量。

1、信号量初始化sem_init

函数原型

对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值

 int sem_init (sem_t *sem , int pshared, unsigned int value);

调用成功时返回0,失败返回-1.

  • sem:初始化的信号量
  • pshared:此信号量是在进程间共享还是线程间共享 (值为0,则表示这个信号量是当前进程的局部信号量)
  • value是信号量的初始值

2、信号量等待sem_wait

函数原型

该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。

如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。直到该信号量值为非0值,相当于P操作。

int sem_wait(sem_t *sem);

调用成功时返回0,失败返回-1.

  • sem:指向的对象是由sem_init调用初始化的信号量

3、发送(释放)信号量sem_post

函数原型

以原子操作的方式将信号量的值加1。当信号量的值大于0时,其它正在调用sem_wait等待信号量的线程将被唤醒,选择机制同样是由线程的调度策略决定的。

int sem_post(sem_t *sem);

调用成功时返回0,失败返回-1.

  • sem:指向的对象是由sem_init调用初始化的信号量

4、测试信号量sem_getvalue

函数原型

int sem_getvalue(sem_t *sem, int *sval);

获取信号量 sem 的当前值,把该值保存在 sval,若有 1 个或者多个线程正在调用 sem_wait 阻塞在该信号量上,该函数返回阻塞在该信号量上进程或线程个数。

5、销毁信号量 sem_destroy

函数原型

用于对用完的信号量的清理

int sem_destroy(sem_t *sem);
  • sem:是要销毁的信号量

只有用sem_init初始化的信号量才能用sem_destroy销毁。

应用实例

演示了如何用信号量同步,模拟一个窗口服务系统。

/* @purpose: 基于信号量的多线程同步,操作系统原理中的P,V操作
 * @author: [email protected]
 * @create: 2015-03-20 Fri
 * */

#include 
#include 
#include 
#include 
#include 


/* @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);
    }
}

#define CUSTOMER_NUM 10

int main(int argc, char *argv[])
{
     
    /* 初始化信号量,初始值为2,表示有两个顾客可以同时接收服务 */
    sem_init(&sem, 0, 2);

    /* 为每个顾客定义一个线程id, pthread_t 其实是unsigned long int */
    pthread_t customers[CUSTOMER_NUM];

    int i, ret;
    /* 为每个顾客生成一个线程 */
    for(i = 0; i < CUSTOMER_NUM; i++){
     
        int customer_id = i;
        ret = pthread_create(&customers[i], NULL, get_service, &customer_id);
        if(ret != 0){
     
            perror("pthread_create");
            exit(1);
        }
        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);
    }

    /* Only a  semaphore that  has been initialized  by sem_init(3)
     * should be destroyed using sem_destroy().*/
    sem_destroy(&sem);
    return 0;
}

linux线程同步方式3——信号量(semaphore)_第1张图片
每次运行都不相同

参考

1、https://www.cnblogs.com/biyeymyhjob/archive/2012/07/21/2602015.html
2、https://blog.csdn.net/qq_19923217/article/details/82902442
3、https://www.cnblogs.com/jiqingwu/p/linux_semaphore_example.html

你可能感兴趣的:(linux,c++,semaphore,linux)