操作系统-线程同步

简介: 操作系统-线程同步

条件变量


条件变量是 GNU/Linux 提供的第三种同步工具(第一互斥体第二这信号量);利用它你可以在多线程环境下实现更复杂的条件控制。


前言


引入条件变量的目的:在使用互斥锁的基础上引入条件变量可以使程序的效率更高,因为条件变量的引入明显减少了线程取竞争互斥锁的次数。执行pthread_cond_wait或pthread_cond_timedwait函数的线程明显知道了条件不满足,要因此在其释放锁之后就没有必要再跟其它线程去竞争锁了,只需要阻塞等待signal或broadcast函数将其唤醒。这样提高了效率。


# 一、线程条件变量是什么?

<font color=#999AAA 条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。


二、条件变量主要函数


主要应用函数


pthread_cond_t类型 用于定义条件变量 pthread_cond_t cond;


pthread_cond_init函数 pthread_cond_destroy函数


pthread_cond_wait函数 pthread_cond_timedwait函数


pthread_cond_signal函数 pthread_cond_broadcast函数


以上6 个函数的返回值都是:成功返回0, 失败直接返回错误号。


1.创建函数

pthread_cond_init函数:条件变量的创建分为静态创建和动态创建;

静态方式使用PTHREAD_COND_INITIALIZER常量,如下:    
 pthread_cond_t   cond=PTHREAD_COND_INITIALIZER    
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
作用:初始化一个条件变量
参2:attr表条件变量属性,通常为默认值,传NULL即可。


2.销毁条件变量

pthread_cond_destroy函数:

int pthread_cond_destroy(
    pthread_cond_t *cond  // 条件变量句柄
    );
作用:销毁一个条件变量


3.条件变量等待

pthread_cond_wait函数:

int pthread_cond_wait(pthread_cond_t *restrict_cond,  //条件变量
            pthread_mutex_t *restrict_mutex   //互斥锁
            );


作用:阻塞等待条件变量cond(形参1)满足条件,且释放已经掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex)(注意这是一个原子操作,即阻塞等待的同时马上解锁,类似sigsuspend函数);当被唤醒(signal或broadcast函数),pthread_cond_wait函数返回,解除阻塞并重新申请获取互斥锁:pthread_mutex_lock(&mutex);


条件等待函数,主要是是实现 解锁-> 唤醒->枷锁


使用:pthread_cond_wati()用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex(互斥锁)配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。


一般在使用的时候都是在一个循环里使用pthread_cond_wait()函数,因为它在返回的时候不一定能拿到锁(这可能会发生饿死情形,当然这取决于操作系统的调度策略


4.条件变量超时等待

pthread_cond_timedwait函数:

int pthread_cond_timedwait(pthread_cond_t *restrict_cond,   //条件变量
      pthread_mutex_t *restrict_mutex,                //互斥锁句柄
      const struct timespec *restrict_abstime);    
参数3:struct timespec结构体。
struct timespec {undefined
time_t tv_sec;               /* seconds */ 秒
long   tv_nsec;       /* nanosecondes*/ 纳秒
}                                                                                 
struct timespec结构体定义的是绝对时间(从1970年1月1日00:00:00开始计时的时间,unix操作系统的诞辰是1969年末)。time(NULL)返回的就是绝对时间(秒)。而alarm(1)是相对时间(相对于当前),相对当前时间定时1秒钟。
struct timespec t = {1, 0};  pthread_cond_timedwait (&cond, &mutex, &t); 只能定时到 1970年1月1日 00:00:01秒(早已经过去)。 1970年1月1日 00:00:00秒作为计时元年。正确用法:
time_t cur = time(NULL);   //获取当前时间(绝对时间)
struct timespec t;             //定义timespec 结构体变量t
t.tv_sec = cur+1;   //定时1秒
pthread_cond_timedwait (&cond, &mutex, &t); 传参   参考APUE.11.6线程同步条件变量小节
在讲解setitimer函数时我们还提到另外一种时间类型:
struct timeval {undefined
time_t      tv_sec;   /* seconds */ 秒
suseconds_t tv_usec;     /* microseconds */ 微秒
};


作用:与pthread_cond_wait函数作用相同,但其限时等待一个条件变量,即如果在规定的时间点(第三个形参)还未被唤醒时,该线程自动唤醒并解除阻塞重新申请获取互斥锁:pthread_mutex_lock(&mutex);


5.单线程唤醒

pthread_cond_signal函数:

int pthread_cond_signal(pthread_cond_t *cond);
作用:唤醒至少一个阻塞在条件变量上的线程。


作用:pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。

使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。

但是pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程.


6.广播唤醒

pthread_cond_broadcast函数:

int pthread_cond_broadcast(pthread_cond_t *cond);
作用:唤醒全部阻塞在条件变量上的线程

编程注意


pthread_cond_signal()函数生效具有前提条件

pthread_cond_signal只能唤醒已经处于pthread_cond_wait的线程;也就是说,如果signal的时候没有线程在condition wait装填,那么本次signal就失效没有效果,后续的线程进入condition wait之后,无法被之前的signal唤醒


例子


    int x,y;
              pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
              pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
      // Waiting until x is greater than y is performed as follows:
              pthread_mutex_lock(&mut);
              while(x <= y){
                      pthread_cond_wait(&cond,&mut);
              }
              /* operate on x and y */
              pthread_mutex_unlock(&mut);
     //  Modifications on x and y that may cause x to become greater than y should signal the con-
     //  dition if needed:
              pthread_mutex_lock(&mut);
              /* modify x and y */
              if(x > y) pthread_cond_broadcast(&cond);
              pthread_mutex_unlock(&mut);
目录
相关文章
|
14天前
|
调度 开发者 Python
深入浅出操作系统:进程与线程的奥秘
在数字世界的底层,操作系统扮演着不可或缺的角色。它如同一位高效的管家,协调和控制着计算机硬件与软件资源。本文将拨开迷雾,深入探索操作系统中两个核心概念——进程与线程。我们将从它们的诞生谈起,逐步剖析它们的本质、区别以及如何影响我们日常使用的应用程序性能。通过简单的比喻,我们将理解这些看似抽象的概念,并学会如何在编程实践中高效利用进程与线程。准备好跟随我一起,揭开操作系统的神秘面纱,让我们的代码运行得更加流畅吧!
|
4月前
|
UED 开发者 Python
探索操作系统的心脏:理解进程与线程
【8月更文挑战第31天】在数字世界的海洋中,操作系统犹如一艘巨轮,其稳定航行依赖于精密的进程与线程机制。本文将揭开这一机制的神秘面纱,通过深入浅出的语言和直观的代码示例,引领读者从理论到实践,体验进程与线程的魅力。我们将从基础概念出发,逐步深入到它们之间的联系与区别,最后探讨如何在编程实践中高效运用这些知识。无论你是初学者还是有经验的开发者,这篇文章都将为你的技术之旅增添新的航标。
|
12天前
|
算法 调度 开发者
深入理解操作系统:进程与线程的管理
在数字世界的复杂编织中,操作系统如同一位精明的指挥家,协调着每一个音符的奏响。本篇文章将带领读者穿越操作系统的幕后,探索进程与线程管理的奥秘。从进程的诞生到线程的舞蹈,我们将一起见证这场微观世界的华丽变奏。通过深入浅出的解释和生动的比喻,本文旨在揭示操作系统如何高效地处理多任务,确保系统的稳定性和效率。让我们一起跟随代码的步伐,走进操作系统的内心世界。
|
1月前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
1月前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
40 2
|
1月前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
3月前
|
存储 消息中间件 资源调度
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
该文章总结了操作系统基础知识中的十个关键知识点,涵盖了进程与线程的概念及区别、进程间通信方式、线程同步机制、死锁现象及其预防方法、进程状态等内容,并通过具体实例帮助理解这些概念。
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
|
2月前
|
算法 安全 调度
深入理解操作系统:进程与线程的管理
【10月更文挑战第9天】在数字世界的心脏跳动着的,不是别的,正是操作系统。它如同一位无形的指挥家,协调着硬件与软件的和谐合作。本文将揭开操作系统中进程与线程管理的神秘面纱,通过浅显易懂的语言和生动的比喻,带你走进这一复杂而又精妙的世界。我们将从进程的诞生讲起,探索线程的微妙关系,直至深入内核,理解调度算法的智慧。让我们一起跟随代码的脚步,解锁操作系统的更多秘密。
39 1
|
1月前
|
Linux 调度
探索操作系统核心:进程与线程管理
【10月更文挑战第24天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是计算机硬件与软件之间的桥梁,更是管理和调度资源的大管家。本文将深入探讨操作系统的两大基石——进程与线程,揭示它们如何协同工作以确保系统运行得井井有条。通过深入浅出的解释和直观的代码示例,我们将一起解锁操作系统的管理奥秘,理解其对计算任务高效执行的影响。
|
3月前
|
资源调度 算法 调度
深入浅出操作系统之进程与线程管理
【9月更文挑战第29天】在数字世界的庞大舞台上,操作系统扮演着不可或缺的角色,它如同一位精通多门艺术的导演,精心指挥着每一个进程和线程的演出。本文将通过浅显的语言,带你走进操作系统的内心世界,探索进程和线程的管理奥秘,让你对这位幕后英雄有更深的了解。