【Linux 线程】线程同步《三》
1、条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥
(1)创建和注销
条件变量和互斥锁一样,都有静态&动态两种创建方式,静态方式使用PTHREAD_COND_INITIALIZER常量,如下:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER
动态方式调用pthread_cond_init()函数,API定义如下:
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)
尽管POSIX标准中为条件变量定义了属性,但在LinuxThreads中没有实现,因此cond_attr值通常为NULL,且被忽略。
注销一个条件变量需要调用pthread_cond_destroy(),只有在没有线程在该条件变量上等待的时候才能注销这个条件变量,否则返回EBUSY。因为Linux实现的条件变量没有分配什么资源,所以注销动作只包括检查是否有等待线程。API定义如下:
int pthread_cond_destroy(pthread_cond_t *cond)
(2)等待和激发
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
等待条件有两种方式:无条件等待pthread_cond_wait()和计时等待pthread_cond_timedwait(),其中计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEOUT,结束等待,其中abstime以与time()系统调用相同意义的绝对时间形式出现,0表示格林尼治时间1970年1月1日0时0分0秒。
无论哪种等待方式,都必须和一个互斥锁配合,以防止多个线程同时请求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的竞争条件(Race Condition)。mutex互斥锁必须是普通锁(PTHREAD_MUTEX_TIMED_NP)或者适应锁(PTHREAD_MUTEX_ADAPTIVE_NP),且在调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列以前,mutex保持锁定状态,并在线程挂起进入等待前解锁。在条件满足从而离开pthread_cond_wait()之前,mutex将被重新加锁,以与进入pthread_cond_wait()前的加锁动作对应。
激发条件有两种形式,pthread_cond_signal()激活一个等待该条件的线程,存在多个等待线程时按入队顺序激活其中一个;而pthread_cond_broadcast()则激活所有等待线程。
(3)互斥变量举例:生产者&消费者

(4)举例:
《举例1》
/*************************************************************************
> File Name: pthread_cond1.c
> Summary: 条件变量应用---生产者&消费者 version1 单个生产者&单个消费者情形
> Author: xuelisheng
> Created Time: 2018年12月18日
************************************************************************/ #include <stdio.h>
#include <unistd.h>
#include <malloc.h>
#include <pthread.h> // 链表作为共享数据,需要被互斥量保护
struct msg{
struct msg *next;
int num;
}; struct msg *head; // 静态初始化,一个条件变量和一个互斥量
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *consumer(void *p)
{
struct msg *mp;
for( ; ; )
{
pthread_mutex_lock(&mutex);
while(head == NULL) // 说明此时没有节点,这里当只有一个消费者时,使用while和if都可以。如果有多个消费者则必须使用while
{
// 一开始 阻塞等待,并解锁mutex
// 收到signal信号之后,解除阻塞,加锁mutex
pthread_cond_wait(&has_product, &mutex);
}
mp = head;
head = mp->next; // 消费掉一个产品
pthread_mutex_unlock(&mutex); printf("Consume %lu --- %d\n", pthread_self(), mp->num);
free(mp);
sleep(rand()%); // 休眠:为了打印效果明显
}
} void *producer(void *p)
{
struct msg *mp;
for(; ;)
{
mp = malloc(sizeof(struct msg));
mp->num = rand() % + ; // 模拟生产一个产品(1-1000之间的一个数字)
printf("Produce ---------------- %d\n", mp->num); // 加互斥锁:mp为共享数据
pthread_mutex_lock(&mutex);
mp->next = head;
head = mp;
pthread_mutex_unlock(&mutex); pthread_cond_signal(&has_product); // 将等待在该条件变量上的一个线程唤醒,通知阻塞在条件变量上的线程
sleep(rand()%); // 休眠:为了打印效果明显
}
} int main()
{
pthread_t pid, cid;
srand(time(NULL)); // 创建线程
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&pid, NULL, consumer, NULL); // 等待回收
pthread_join(pid, NULL);
pthread_join(cid, NULL); return ;
}
运行结果(截取部分):
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Produce ----------------
Consume ---
Produce ----------------
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Produce ----------------
Produce ----------------
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Consume ---
《举例2》
/*************************************************************************
> File Name: pthread_cond2.c
> Summary: 条件变量应用---生产者&消费者 version2 单个生产者&多个消费者情形
> Author: xuelisheng
> Created Time: 2018年12月18日
************************************************************************/ #include <stdio.h>
#include <unistd.h>
#include <malloc.h>
#include <pthread.h> // 链表作为共享数据,需要被互斥量保护
struct msg{
struct msg *next;
int num;
}; struct msg *head; // 静态初始化,一个条件变量和一个互斥量
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void *consumer(void *p)
{
struct msg *mp;
for( ; ; )
{
pthread_mutex_lock(&mutex);
while(head == NULL) // 说明此时没有节点,这里当只有一个消费者时,使用while和if都可以。如果有多个消费者则必须使用while
{
// 一开始 阻塞等待,并解锁mutex
// 收到signal信号之后,解除阻塞,加锁mutex
pthread_cond_wait(&has_product, &mutex); // 多个消费者线程都阻塞在这里
}
mp = head;
head = mp->next; // 消费掉一个产品
pthread_mutex_unlock(&mutex); printf("Consume %lu --- %d\n", pthread_self(), mp->num);
free(mp);
sleep(rand()%); // 休眠:为了打印效果明显
}
} void *producer(void *p)
{
struct msg *mp;
for(; ;)
{
mp = malloc(sizeof(struct msg));
mp->num = rand() % + ; // 模拟生产一个产品(1-1000之间的一个数字)
printf("Produce ---------------- %d\n", mp->num); // 加互斥锁:mp为共享数据
pthread_mutex_lock(&mutex);
mp->next = head;
head = mp;
pthread_mutex_unlock(&mutex); pthread_cond_signal(&has_product); // 将等待在该条件变量上的一个线程唤醒,通知阻塞在条件变量上的线程
sleep(rand()%); // 休眠:为了打印效果明显
}
} int main()
{
pthread_t pid, cid;
srand(time(NULL)); // 创建线程
pthread_create(&pid, NULL, producer, NULL); // 创建多个消费者
pthread_create(&pid, NULL, consumer, NULL);
pthread_create(&pid, NULL, consumer, NULL);
pthread_create(&pid, NULL, consumer, NULL);
pthread_create(&pid, NULL, consumer, NULL); // 等待回收
pthread_join(pid, NULL);
pthread_join(cid, NULL); return ;
}
运行结果(截取部分):发现消费者线程id不同,即多个消费者
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Consume ---
Produce ----------------
Produce ----------------
Consume ---
Consume ---
Produce ----------------
Consume ---
【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)系统判断账户金额是否大 ...
随机推荐
- python 之列表推导式,集合推导式,以及字典推导式
https://www.cnblogs.com/weihengblog/p/8428124.html
- UnicodeDecodeError: 'ascii' codec can't decode byte 0x9c in position 1: ordinal not in range(128)
待研究: compressed_data = zlib.compress(json.dumps(data), 9) file_data = MySQLdb.escape_string(compress ...
- datasnap服务器支持的参数类型
可作为参数的类型TDBXWideStringValueTDBXAnsiStringValueTDBXInt16ValueTDBXInt32ValueTDBXInt64ValueTDBXSingleVa ...
- git reset 版本回退
git log 查看所有提交信息. commit 67692318180bed6b2a17db0708cfbe0231e33db3 (HEAD -> master) Author: kingBo ...
- java由字符型强制转化为整型例题
此Java程序依次输出参数,参数类型为字符型,要求更改程序,使得字符型强制转化为整形,并将这些整数相加,最后输出总和. 原程序: package demo; public class CommandP ...
- Rust语言学习笔记(7)
模块 // 兄弟模块 mod network { fn connect() { } } mod client { fn connect() { } } // 父子模块 mod network { fn ...
- ReactiveX 学习笔记(14)使用 RxJava2 + Retrofit2 调用 REST API
JSON : Placeholder JSON : Placeholder (https://jsonplaceholder.typicode.com/) 是一个用于测试的 REST API 网站. ...
- Redis进阶实践之二如何在Linux系统上安装安装Redis(转载)(2)
Redis进阶实践之二如何在Linux系统上安装安装Redis 一.引言 上一篇文章写了“如何安装VMware Pro虚拟机”和在虚拟机上安装Linux操作系统.那是第一步,有了Linux操作系统,我 ...
- Node稳定性的研究心得
目前大部分Web服务器,如Apache,都使用多线程的方式响应多用户请求,即一个线程服务一个用户请求.这种模式其中一个好处是,当某个请求的线程上抛出的异常没被捕获,只会影响当前这个线程,不会影响其他请 ...
- LeetCode OJ 24. Swap Nodes in Pairs
Given a linked list, swap every two adjacent nodes and return its head. For example,Given 1->2-&g ...