【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》

 1 /*************************************************************************
 2     > File Name: pthread_cond1.c
 3     > Summary: 条件变量应用---生产者&消费者 version1 单个生产者&单个消费者情形
 4     > Author: xuelisheng
 5     > Created Time: 2018年12月18日
 6  ************************************************************************/
 7
 8 #include <stdio.h>
 9 #include <unistd.h>
10 #include <malloc.h>
11 #include <pthread.h>
12
13 // 链表作为共享数据,需要被互斥量保护
14 struct msg{
15     struct msg *next;
16     int num;
17 };
18
19 struct msg *head;
20
21 // 静态初始化,一个条件变量和一个互斥量
22 pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
23 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
24
25 void *consumer(void *p)
26 {
27     struct msg *mp;
28     for( ; ; )
29     {
30         pthread_mutex_lock(&mutex);
31         while(head == NULL)                    // 说明此时没有节点,这里当只有一个消费者时,使用while和if都可以。如果有多个消费者则必须使用while
32         {
33              // 一开始 阻塞等待,并解锁mutex
34              // 收到signal信号之后,解除阻塞,加锁mutex
35              pthread_cond_wait(&has_product, &mutex);
36         }
37         mp = head;
38         head = mp->next;                                                                                  // 消费掉一个产品
39         pthread_mutex_unlock(&mutex);
40
41         printf("Consume %lu --- %d\n", pthread_self(), mp->num);
42         free(mp);
43         sleep(rand()%5);                                                                                  // 休眠:为了打印效果明显
44     }
45 }
46
47  void *producer(void *p)
48  {
49      struct msg *mp;
50      for(; ;)
51      {
52          mp = malloc(sizeof(struct msg));
53          mp->num = rand() % 1000 + 1;                         // 模拟生产一个产品(1-1000之间的一个数字)
54          printf("Produce ---------------- %d\n", mp->num);
55
56          // 加互斥锁:mp为共享数据
57          pthread_mutex_lock(&mutex);
58          mp->next = head;
59          head = mp;
60          pthread_mutex_unlock(&mutex);
61
62          pthread_cond_signal(&has_product);                            // 将等待在该条件变量上的一个线程唤醒,通知阻塞在条件变量上的线程
63          sleep(rand()%5);                                                                           // 休眠:为了打印效果明显
64      }
65  }
66
67  int main()
68  {
69      pthread_t pid, cid;
70      srand(time(NULL));
71
72      // 创建线程
73      pthread_create(&pid, NULL, producer, NULL);
74      pthread_create(&pid, NULL, consumer, NULL);
75
76      // 等待回收
77      pthread_join(pid, NULL);
78      pthread_join(cid, NULL);
79
80      return 0;
81  }

运行结果(截取部分):

Produce ---------------- 395
Consume 140093303256832 --- 395
Produce ---------------- 506
Consume 140093303256832 --- 506
Produce ---------------- 553
Consume 140093303256832 --- 553
Produce ---------------- 139
Produce ---------------- 758
Consume 140093303256832 --- 758
Produce ---------------- 313
Produce ---------------- 267
Consume 140093303256832 --- 267
Produce ---------------- 739
Consume 140093303256832 --- 739
Produce ---------------- 718
Consume 140093303256832 --- 718
Consume 140093303256832 --- 313
Produce ---------------- 744
Consume 140093303256832 --- 744
Consume 140093303256832 --- 139
Produce ---------------- 619
Produce ---------------- 449
Produce ---------------- 948
Produce ---------------- 276
Consume 140093303256832 --- 276
Produce ---------------- 896
Consume 140093303256832 --- 896
Consume 140093303256832 --- 948
Produce ---------------- 837
Produce ---------------- 317
Consume 140093303256832 --- 317
Produce ---------------- 478
Consume 140093303256832 --- 478
Consume 140093303256832 --- 837
Produce ---------------- 545
Consume 140093303256832 --- 545

《举例2》

 1 /*************************************************************************
 2     > File Name: pthread_cond2.c
 3     > Summary: 条件变量应用---生产者&消费者 version2 单个生产者&多个消费者情形
 4     > Author: xuelisheng
 5     > Created Time: 2018年12月18日
 6  ************************************************************************/
 7
 8 #include <stdio.h>
 9 #include <unistd.h>
10 #include <malloc.h>
11 #include <pthread.h>
12
13 // 链表作为共享数据,需要被互斥量保护
14 struct msg{
15     struct msg *next;
16     int num;
17 };
18
19 struct msg *head;
20
21 // 静态初始化,一个条件变量和一个互斥量
22 pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
23 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
24
25 void *consumer(void *p)
26 {
27     struct msg *mp;
28     for( ; ; )
29     {
30         pthread_mutex_lock(&mutex);
31         while(head == NULL)                    // 说明此时没有节点,这里当只有一个消费者时,使用while和if都可以。如果有多个消费者则必须使用while
32         {
33              // 一开始 阻塞等待,并解锁mutex
34              // 收到signal信号之后,解除阻塞,加锁mutex
35              pthread_cond_wait(&has_product, &mutex);               // 多个消费者线程都阻塞在这里
36         }
37         mp = head;
38         head = mp->next;                                                                                  // 消费掉一个产品
39         pthread_mutex_unlock(&mutex);
40
41         printf("Consume %lu --- %d\n", pthread_self(), mp->num);
42         free(mp);
43         sleep(rand()%5);                                                                                  // 休眠:为了打印效果明显
44     }
45 }
46
47  void *producer(void *p)
48  {
49      struct msg *mp;
50      for(; ;)
51      {
52          mp = malloc(sizeof(struct msg));
53          mp->num = rand() % 1000 + 1;                         // 模拟生产一个产品(1-1000之间的一个数字)
54          printf("Produce ---------------- %d\n", mp->num);
55
56          // 加互斥锁:mp为共享数据
57          pthread_mutex_lock(&mutex);
58          mp->next = head;
59          head = mp;
60          pthread_mutex_unlock(&mutex);
61
62          pthread_cond_signal(&has_product);                            // 将等待在该条件变量上的一个线程唤醒,通知阻塞在条件变量上的线程
63          sleep(rand()%5);                                                                           // 休眠:为了打印效果明显
64      }
65  }
66
67  int main()
68  {
69      pthread_t pid, cid;
70      srand(time(NULL));
71
72      // 创建线程
73      pthread_create(&pid, NULL, producer, NULL);
74
75      // 创建多个消费者
76      pthread_create(&pid, NULL, consumer, NULL);
77      pthread_create(&pid, NULL, consumer, NULL);
78      pthread_create(&pid, NULL, consumer, NULL);
79      pthread_create(&pid, NULL, consumer, NULL);
80
81      // 等待回收
82      pthread_join(pid, NULL);
83      pthread_join(cid, NULL);
84
85      return 0;
86  }

运行结果(截取部分):发现消费者线程id不同,即多个消费者

Produce ---------------- 913
Consume 139785213572864 --- 913
Produce ---------------- 509
Consume 139785213572864 --- 509
Produce ---------------- 970
Consume 139785292695296 --- 970
Produce ---------------- 3
Consume 139785196787456 --- 3
Produce ---------------- 41
Consume 139785205180160 --- 41
Produce ---------------- 917
Consume 139785213572864 --- 917
Produce ---------------- 417
Consume 139785196787456 --- 417
Produce ---------------- 768
Consume 139785292695296 --- 768
Produce ---------------- 354
Consume 139785213572864 --- 354
Produce ---------------- 706
Consume 139785205180160 --- 706
Produce ---------------- 412
Consume 139785292695296 --- 412
Produce ---------------- 359
Consume 139785196787456 --- 359
Produce ---------------- 144
Produce ---------------- 400
Consume 139785213572864 --- 144
Consume 139785205180160 --- 400
Produce ---------------- 809
Consume 139785213572864 --- 809

原文地址:https://www.cnblogs.com/xuelisheng/p/10138915.html

时间: 2024-10-14 14:14:22

【Linux 线程】线程同步《三》的相关文章

linux 线程的同步 三 (信号量的使用)

信号量.同步这些名词在进程间通信时就已经说过,在这里它们的意思是相同的,只不过是同步的对象不同而已.但是下面介绍的信号量的接口是用于线程的信号量,注意不要跟用于进程间通信的信号量混淆,关于用于进程间通信的信号量的详细介绍可以参阅我的另一篇博文:Linux进程间通信——使用信号量.相似地,线程同步是控制线程执行和访问临界区域的方法. 一.什么是信号量 线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作.如果一个程序中有多个线

Linux之线程同步

一.整体大纲 二.线程同步 1. 同步概念 所谓同步,即同时起步,协调一致.不同的对象,对“同步”的理解方式略有不同.如,设备同步,是指在两个设备之间规定一个共同的时间参考:数据库同步,是指让两个或多个数据库内容保持一 致,或者按需要部分保持一致:文件同步,是指让两个或多个文件夹里的文件保持一致等等. 而编程中.通信中所说的同步与生活中大家印象中的同步概念略有差异.“同”字应是指协同.协助.互相配合.主旨在协同步调,按预定的先后次序运行. 2. 线程同步 (1)线程同步概念 同步即协同步调,按预

Linux环境下线程消息同步的陷阱

我们程序中常常会使用到线程间的消息同步处理,比如以下一段伪码 var message = "": void func()  {   1. 启动线程Thread(该线程中填充message的内容):   2. 阻塞,直到等待到完成message填充的事件:   3. 处理message:   .... } void Thread()  {   1. 通过某种处理填充message:   2. 触发func中的阻塞事件: } 我们通常会使用条件变量来完成类似情况的线程同步处理 比如wind

Linux下线程的同步与互斥

一.线程的互斥 多个线程同时访问共享数据时可能会冲突,跟之前信号量的可重如性是同样的问题.如两个线程都要把某个全局变量增加1,这个操作在某平台需要三条指令完成: 1. 从内存读变量值到寄存器 2. 寄存器的值加1 3. 将寄存器的值写回内存 如下程序就会产生问题: 我们创建两个线程,每把g_count增加5000次,正常情况下最后g_count应该等于10000,但事实上每次运行该程序的结果都不一样. 对于多线程的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁(Mutex,Mutual

Linux进程间通信与线程间同步详解(全面详细)

引用:http://community.csdn.net/Expert/TopicView3.asp?id=4374496linux下进程间通信的几种主要手段简介: 1. 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信:   2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身

linux线程间同步方式汇总

抽空做了下linux所有线程间同步方式的汇总(原生的),包含以下几个: 1, mutex 2, condition variable 3, reader-writer lock 4, spin lock 5, barrier mutex是最常用的线程间同步方式,主要目的是保护共享的资源可以被原子地访问. 个人感觉condition variable是除了mutex之外的第二常用的线程间同步方式,可以用来以同步的方式使用一个线程来通知另一个线程某个事件已经发生.可以理解为线程间的信号. reade

Linux/UNIX线程控制

线程控制 线程属性 调用pthread_create函数的例子中,传入的参数都是空指针,而不是指向pthread_attr_t结果的指针.可以用pthread_attr_t结构修改线程默认属性,并把这些属性与创建的线程联系起来.可以使用pthread_attr_init函数初始化pthreaad_attr_t结构.调用pthread_attr_init以后,pthread_attr_t结构所包含的内容就是操作系统实现支持的线程所有属性的默认值.如果要修改其中个别属性的值,需要调用其他的函数.pt

Linux编程---线程

首先说一下线程的概念.其实就是运行在进程的上下文环境中的一个执行流.普通进程只有一条执行流,但是线程提供了多种执行的路径并行的局面. 同时,线程还分为核心级线程和用户级线程.主要区别在属于核内还是核外. 核心级线程,地位基本和进程相当,由内核调度.也就是说这种系统时间片是按线程来分配的.这种线程的好处就是可以适当的运用SMP,即针对多核CPU进行调度. 用户级线程,在用户态来调度.所以相对来说,切换的调度时间相对核心级线程来说要快不少.但是不能针对SMP进行调度. 对于现在的系统来说,纯粹的用户

Linux环境编程之同步(三):读写锁

概述 互斥锁把试图进入我们称之为临界区的所有其他线程都阻塞住.该临界区通常涉及对由这些线程共享一个或多个数据的访问或更新.读写锁在获取读写锁用于读某个数据和获取读写锁用于写直接作区别.读写锁的分配规则如下: 1.只要没有线程持有某个给定的读写锁用于写,那么任意数目的线程可以持有该读写锁用于读. 2.仅当没有线程持有某个给定的读写锁用于读或用于写时,才能分配该读写锁用于写. 即只要没有线程在修改某个给定的数据,那么任意数目的线程都可以拥有该数据的读访问权.仅当没有其他线程在读或修改某个给定的数据时

Linux 下线程的理解

2017-04-03 最近深入研究了下Linux线程的问题,发现自己之前一直有些许误解,特记之-- 关于Linux下的线程,各种介绍Linux的书籍都没有深入去解释的,或许真的如书上所述,Linux本质上不存在线程的概念!在某种程度上的确是这样,但是难道LInux就只有一种进程的东西么??答案肯定是否定的!下面咱们慢慢分析 说起Linux下的线程,的确不如windows下来的直接,windwos中进程和线程有着明确的区分,各自有自己代表的数据结构.操作API,进程享有资源和线程参加调度.一切都是