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. UICollectionView setPrefetchingEnabled

    UICollectionView 开启是否开启预加载,如果开启,cell在没显示的时候就回去调用cellForIndex…方法,如果没开启,cell只有在显示的时候才会去调用cellForIndex… ...

  2. 与前端对接 jsonp

    主要是回调的写法,前端人员接受的数据格式      参数 (jsonString);

  3. JAVA_Package

    Javaの名前空間の仕組みの1つにパッケージがあります.大規模開発では必須の概念です.また.他人の作ったコードの再利用という観点でも.パッケージを正しく活用する必要があります. ・完全修飾名:パッケー ...

  4. 在Ubuntu 16.04 上编译安装OpenCV3.2.0(Cmake + python3 + OpenCV3)(转)

    1 安装CMAKE sudo apt-get install cmake 2 安装python及其所依赖的软件包 sudo apt-get install build-essential sudo a ...

  5. 一次UNITY闪退问题的定位心得

    最近项目测试发现,运行unity后不退出运行模式,玩了一局后点击 “再来一局”,反复十几局后unity崩掉. 经观察,发现在这十几局的过程中,unity占用内存不断上升,由3.2G左右上升到3.6G左 ...

  6. 剑指OFFER例题——从尾到头打印链表

    /** * public class ListNode { * int val; * ListNode next = null; * * ListNode(int val) { * this.val ...

  7. 第六次Scrum冲刺

    第六次Scrum冲刺 1.成员今日完成情况 队员 今日完成任务 刘佳 前端构建 李佳 后端设计 周世元 数据设计 杨小妮 博客编写 许燕婷 管理团队当日及次日任务 陈水莲 性能测试用例设计 曾丽丽 性 ...

  8. windows 10 专业版 激活

    参考文章:https://jingyan.baidu.com/article/c14654134b99de0bfcfc4c8c.html http://www.windowszj.com/news/2 ...

  9. Docker虚拟化平台

    1.虚拟化技术的概念 1)虚拟化就是把物理资源转变为逻辑上可以管理的资源,以打破物理结构间的壁垒,让计算机的元件运行在虚拟的基础上,而不是真实的物理设备: 2)虚拟化技术可以将物理机硬件资源虚拟生成单 ...

  10. 获取当前ip

    测ip地址http://2018.ip138.com/ic.asphttps://www.ip.cn/