Linux 线程】线程同步《四》
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 线程】线程同步《四》的更多相关文章
- linux 线程的同步 三 (内存信号量的使用)
信号量.同步这些名词在进程间通信时就已经说过,在这里它们的意思是相同的,只不过是同步的对象不同而已.但是下面介绍的信号量的接口是用于线程的信号量,注意不要跟用于进程间通信的信号量混淆,关于用于进程间通 ...
- Linux 多线程 - 线程异步与同步机制
Linux 多线程 - 线程异步与同步机制 I. 同步机制 线程间的同步机制主要包括三个: 互斥锁:以排他的方式,防止共享资源被并发访问:互斥锁为二元变量, 状态为0-开锁.1-上锁;开锁必须由上锁的 ...
- linux线程间同步方式总结梳理
线程间一般无需特别的手段进行通信,由于线程间能够共享数据结构,也就是一个全局变量能够被两个线程同时使用.只是要注意的是线程间须要做好同步! 使用多线程的理由: 1. 一个是和进程相比,它是一种非常&q ...
- Linux线程间同步的几种方式
信号量 信号量强调的是线程(或进程)间的同步:"信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在sem_wait的时候,就阻塞 ...
- Linux/Unix 线程同步技术之互斥量(1)
众所周知,互斥量(mutex)是同步线程对共享资源访问的技术,用来防止下面这种情况:线程A试图访问某个共享资源时,线程B正在对其进行修改,从而造成资源状态不一致.与之相关的一个术语临界区(critic ...
- Linux的线程同步对象:互斥量Mutex,读写锁,条件变量
进程是Linux资源分配的对象,Linux会为进程分配虚拟内存(4G)和文件句柄等 资源,是一个静态的概念.线程是CPU调度的对象,是一个动态的概念.一个进程之中至少包含有一个或者多个线程.这 ...
- Linux系统编程(29)——线程间同步(续篇)
线程间的同步还有这样一种情况:线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行.在pthread库中通过条件变 ...
- linux线程间同步方式汇总
抽空做了下linux所有线程间同步方式的汇总(原生的),包含以下几个: 1, mutex 2, condition variable 3, reader-writer lock 4, spin loc ...
- java并发编程(三)----线程的同步
在现实开发中,我们或多或少的都经历过这样的情景:某一个变量被多个用户并发式的访问并修改,如何保证该变量在并发过程中对每一个用户的正确性呢?今天我们来聊聊线程同步的概念. 一般来说,程序并行化是为了获得 ...
- 深入Java线程管理(三):线程同步
一. 引入同步: 有一个很经典的案例,即银行取款问题.我们可以先看下银行取款的基本流程: 1)用户输入账户.密码,系统判断用户的账户.密码是否匹配. 2)用户输入取款金额. 3)系统判断账户金额是否大 ...
随机推荐
- C#中关于@的用法
1. 加在字符串前面,字符串中的 \ 失去转义符的作用,直接写字符串而不需要考虑转义字符 string path = @"C:\Windows\"; // 如果不加 @,编译会提示 ...
- leetcode 题解: Gray Code
第一眼看到就是枚举,回溯法. n位的ans就是在n-1的ans的基础上,每一个在首位加上1. 但是有个难点,要保证相邻两数之间只有一位在变化,怎么办? 首先 00 00 01 00 01 11 10 ...
- DataGrip 连接数据库查询出来的结果乱码的问题
打开连接数据源选项 选择 Advanced----Charset 填入 GBK 应用即可 目前遇到的是连接 SYbase数据库
- Eclipse SVN文件冲突及不能直接提交情况
下图为Eclipse SVN使用过程中存在文件冲突的情形. 以下是三种冲突情形及相应解决办法: 1.简单的文件版本冲突 情形:A改变了文件的头部,B改变了文件的尾部,如果两者改动互不影响,SVN可以智 ...
- linux shell实现 URL 编码/解码方法
(1)编码的两种方法 # echo '手机' | tr -d '\n' | xxd -plain | sed 's/\(..\)/%\1/g' # echo '手机' |tr -d '\n' |od ...
- linux 内存布局以及tlb更新的一些理解
问题: 1.内核线程是否有vma线性区? 2.单线程的一个进程,它修改了自己的页表,是否需要发送ipi来通知其他核更新tlb? 3.普通进程,在32位和64位,对应的线性区的最大地址能到多少? 在64 ...
- java 中 Integer 传参方式的问题
Java本身都是值传递式的调用,对于对象传递的是地址值.给地址值重新赋值等于重新指向,不会影响外层. 而且这里Integer对象也有特殊性.其实现上可能类似 class Integer{ final ...
- Haskell语言学习笔记(83)Pipes
安装 pipes $ cabal install pipes Installed pipes-4.3.9 Prelude> import Pipes Prelude Pipes> impo ...
- 转:探讨JS合并两个数组的方法
我们在项目过程中,有时候会遇到需要将两个数组合并成为一个的情况. 比如: var a = [1,2,3]; var b = [4,5,6]; 有两个数组a.b,需求是将两个数组合并成一个.方法如下: ...
- oracle函数大全-字符处理函
字符函数——返回字符值 这些函数全都接收的是字符族类型的参数(CHR 除外)并且返回字符值.除了特别说明的之外,这些函数大部分返回VARCHAR2类型的数值.字符函数的返回类型所受的限制和基本数据库类 ...