1、信号量

(1)概念

信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区

不多做解释,要使用信号量同步,需要包含头文件semaphore.h。

主要用到的函数:

  • int sem_init(sem_t *sem, int pshared, unsigned int value);其中sem是要初始化的信号量,pshared表示此信号量是在进程间共享还是线程间共享,value是信号量的初始值。
  • int sem_destroy(sem_t *sem);其中sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。
  • int sem_wait(sem_t *sem);等待信号量,如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。
  • int sem_post(sem_t *sem); 释放信号量,让信号量的值加1。相当于V操作。

(2)举例

《举例1》

 /*************************************************************************
> File Name: semTest1.c
> Summary: 信号量实现生产者&消费者模型
> Author: xuelisheng
> Created Time: 2018年12月18日
************************************************************************/ #include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h> #define NUM 5 int queue[NUM]; // 全局数组实现环形队列
sem_t blank_number, product_number; // 定义2个信号量:空格子信号量 产品信号量 void *producer(void *arg)
{
int i = ;
while()
{
// int sem_post(sem_t *sem); 释放信号量,让信号量的值加1。相当于V操作。
sem_wait(&blank_number); // 生产者将空格字数 -- ,为0则阻塞等待
queue[i] = rand() % + ; // 生产一个产品
printf("-----producer-----%d i = %d\n",queue[i], i);
sem_post(&product_number); // 将产品数 ++ 唤醒 i = (i+) % NUM; // 借助下标实现环形队列
sleep(rand() % );
}
} void *consumer(void *arg)
{
int i = ;
while()
{
// int sem_wait(sem_t *sem);等待信号量,如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。
sem_wait(&product_number); // 消费者将产品信号量数 --,为0则阻塞等待
printf("-----consumer-----%d i = %d\n", queue[i], i);
queue[i] = ; // 消费一个产品(填充0表示)
sem_post(&blank_number); // 消费掉之后,将格子数 ++ i= (i+) % NUM;
sleep(rand() % );
}
} int main()
{
pthread_t pid, cid; // int sem_init(sem_t *sem, int pshared, unsigned int value);,其中sem是要初始化的信号量,pshared表示此信号量是在进程间共享还是线程间共享(0表示线程),value是信号量的初始值。
sem_init(&blank_number, , NUM);
sem_init(&product_number, , ); pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL); pthread_join(pid, NULL);
pthread_join(cid, NULL); // int sem_destroy(sem_t *sem); 其中sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。
sem_destroy(&blank_number);
sem_destroy(&product_number); return ;
}

运行结果:

-----producer-----          i =
-----consumer----- i =
-----producer----- i =
-----producer----- i =
-----producer----- i =
-----producer----- i =
-----producer----- i =
-----consumer----- i =
-----producer----- i =
-----consumer----- i =
-----producer----- i =
-----consumer----- i =
-----producer----- i =
-----consumer----- i =
-----producer----- i =
-----consumer----- i =
-----producer----- i =

《举例2》

 /*************************************************************************
> File Name: semTest2.c
> Summary: 基于信号量的多线程同步,操作系统原理中的P,V操作
> Author: xuelisheng
> Created Time: 2018年12月18日
************************************************************************/
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> /* @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) == ) // 对共享资源的控制(每次只能2个线程对共享资源进行访问)
{
usleep(); /* 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,表示有两个顾客可以同时接收服务 */
/* @prototype: int sem_init(sem_t *sem, int pshared, unsigned int value); */
/* pshared: if pshared == 0, the semaphore is shared among threads of a process
* otherwise the semaphore is shared between processes. */
sem_init(&sem, , ); /* 为每个顾客定义一个线程id, pthread_t 其实是unsigned long int */
// 定义10个线程
pthread_t customers[CUSTOMER_NUM]; int i, ret;
/* 为每个顾客生成一个线程 */
for(i = ; i < CUSTOMER_NUM; i++)
{
int customer_id = i;
ret = pthread_create(&customers[i], NULL, get_service, &customer_id);
if(ret != )
{
perror("pthread_create");
exit();
}
else
{
printf("Customer %d arrived.\n", i);
}
usleep();
} /* 等待所有顾客的线程结束 */
/* 注意:这地方不能再用i做循环变量,因为可能线程中正在访问i的值 */
int j;
for(j = ; 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 ;
}

运行结果:(结果不唯一)

Customer  arrived.
Customer arrived.
customer receive service ...
Customer arrived.
customer receive service ...
Customer arrived.
customer receive service ...
Customer arrived.
customer receive service ...
Customer arrived.
customer receive service ...
Customer arrived.
customer receive service ...
Customer arrived.
customer receive service ...
Customer arrived.
customer receive service ...
Customer arrived.
customer receive service ...
customer receive service ...

参考:https://www.cnblogs.com/jiqingwu/p/linux_semaphore_example.html

Linux 线程】线程同步《四》的更多相关文章

  1. linux 线程的同步 三 (内存信号量的使用)

    信号量.同步这些名词在进程间通信时就已经说过,在这里它们的意思是相同的,只不过是同步的对象不同而已.但是下面介绍的信号量的接口是用于线程的信号量,注意不要跟用于进程间通信的信号量混淆,关于用于进程间通 ...

  2. Linux 多线程 - 线程异步与同步机制

    Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...

  3. linux线程间同步方式总结梳理

    线程间一般无需特别的手段进行通信,由于线程间能够共享数据结构,也就是一个全局变量能够被两个线程同时使用.只是要注意的是线程间须要做好同步! 使用多线程的理由: 1. 一个是和进程相比,它是一种非常&q ...

  4. Linux线程间同步的几种方式

    信号量 信号量强调的是线程(或进程)间的同步:"信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在sem_wait的时候,就阻塞 ...

  5. Linux/Unix 线程同步技术之互斥量(1)

    众所周知,互斥量(mutex)是同步线程对共享资源访问的技术,用来防止下面这种情况:线程A试图访问某个共享资源时,线程B正在对其进行修改,从而造成资源状态不一致.与之相关的一个术语临界区(critic ...

  6. Linux的线程同步对象:互斥量Mutex,读写锁,条件变量

        进程是Linux资源分配的对象,Linux会为进程分配虚拟内存(4G)和文件句柄等 资源,是一个静态的概念.线程是CPU调度的对象,是一个动态的概念.一个进程之中至少包含有一个或者多个线程.这 ...

  7. Linux系统编程(29)——线程间同步(续篇)

    线程间的同步还有这样一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行.在pthread库中通过条件变 ...

  8. linux线程间同步方式汇总

    抽空做了下linux所有线程间同步方式的汇总(原生的),包含以下几个: 1, mutex 2, condition variable 3, reader-writer lock 4, spin loc ...

  9. java并发编程(三)----线程的同步

    在现实开发中,我们或多或少的都经历过这样的情景:某一个变量被多个用户并发式的访问并修改,如何保证该变量在并发过程中对每一个用户的正确性呢?今天我们来聊聊线程同步的概念. 一般来说,程序并行化是为了获得 ...

  10. 深入Java线程管理(三):线程同步

    一. 引入同步: 有一个很经典的案例,即银行取款问题.我们可以先看下银行取款的基本流程: 1)用户输入账户.密码,系统判断用户的账户.密码是否匹配. 2)用户输入取款金额. 3)系统判断账户金额是否大 ...

随机推荐

  1. csv操作

    需要引入javacsv.jar 以下为一个完整的Utils的写法,具体输出和输入需要自己修改参数. import java.io.File; import java.io.FileNotFoundEx ...

  2. IntelliJ IDEA 自动导入包 关闭重复代码提示

    idea可以自动优化导入包,但是有多个同名的类调用不同的包,必须自己手动Alt+Enter设置 设置idea导入包 勾选标注 1 选项,IntelliJ IDEA 将在我们书写代码的时候自动帮我们优化 ...

  3. pod引用第三方库的几种方式

    pod引用库的原理,本质上是去找.podspec文件,podspec中包含库的地址及最新的版本号(tag标签),如果pod时没有指定版本,则pod install时会去下载podspec文件中指定的最 ...

  4. Zeosdbo-Query使用

    with DataModule1.Zlxz_zy_Query do        begin          Close;          SQL.Clear;          SQL.Add( ...

  5. PropertiesUtil

    PropertiesUtil package com.zjx.util; import java.io.IOException; import java.io.InputStream; import ...

  6. pip安装提示PermissionError: [WinError 5]错误问题解决

    操作环境   Python3.6 + Winodws7 问题现象 新安装python3.6版本后使用pip安装第三方模块失败,报错信息如下: C:\Users\linyfeng>pip inst ...

  7. [C语言]使用数组

    ----------------------------------------------------------------------------- // main.c 例 // Created ...

  8. [C语言]逆序的三位数

    ---------------------------------------------------------------------------------------- //  main.c ...

  9. 拓展jquery js动态添加html代码 初始化数据

    1 /** * 新增数据筛选 */ (function () { $.filterEvent = function(options){ var _this = this; var defaults = ...

  10. SimpleDateFormat 使用时出现的线程同步问题。。。

    错误使用: public static final SimpleDateFormat DAY_UI_MONTH_DAY_FORMAT = new SimpleDateFormat("MM-d ...