操作系统-线程同步

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

条件变量


条件变量是 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);
目录
相关文章
|
28天前
|
UED 开发者 Python
探索操作系统的心脏:理解进程与线程
【8月更文挑战第31天】在数字世界的海洋中,操作系统犹如一艘巨轮,其稳定航行依赖于精密的进程与线程机制。本文将揭开这一机制的神秘面纱,通过深入浅出的语言和直观的代码示例,引领读者从理论到实践,体验进程与线程的魅力。我们将从基础概念出发,逐步深入到它们之间的联系与区别,最后探讨如何在编程实践中高效运用这些知识。无论你是初学者还是有经验的开发者,这篇文章都将为你的技术之旅增添新的航标。
|
3月前
|
存储 调度 C++
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
88 1
|
13天前
|
开发者 Python
深入浅出操作系统:进程与线程的奥秘
【8月更文挑战第46天】在数字世界的幕后,操作系统扮演着至关重要的角色。本文将揭开进程与线程这两个核心概念的神秘面纱,通过生动的比喻和实际代码示例,带领读者理解它们的定义、区别以及如何在编程中运用这些知识来优化软件的性能。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和实用技巧。
|
30天前
|
调度
深入理解操作系统:进程与线程的管理
【8月更文挑战第29天】在数字世界的每一次点击和滑动背后,都隐藏着操作系统的精妙运作。本文将带你探索操作系统的核心概念之一——进程与线程的管理。我们将从基础定义出发,逐步深入到它们在内存中的表示、状态变迁以及它们之间错综复杂的关系。通过简洁明了的语言和直观的比喻,即便是没有计算机背景的读者也能轻松理解这一主题。准备好了吗?让我们一起揭开操作系统神秘的面纱,探索那些看似晦涩却无比精彩的知识吧!
|
1月前
|
调度 Python
深入理解操作系统:进程与线程的奥秘
【8月更文挑战第27天】本文将带你走进操作系统的核心,探索进程和线程这两个基本概念。我们将从它们的定义开始,逐步深入到它们之间的联系和区别,以及在操作系统中的作用。通过本文,你将了解到进程和线程不仅仅是编程中的两个术语,它们是操作系统管理资源、实现并发和并行的关键。最后,我们还将通过一个代码示例,展示如何在Python中创建和管理线程。
|
1月前
|
算法 调度 开发者
深入理解操作系统:进程与线程管理
【8月更文挑战第28天】在数字世界的心脏跳动着的是操作系统,它是计算机硬件与软件之间的桥梁。本文将带你探索操作系统的核心概念——进程与线程,揭示它们如何协同工作以支持多任务处理和并发执行。通过实际代码示例,我们将深入了解这些抽象概念是如何在真实系统中实现的。无论你是编程新手还是资深开发者,这篇文章都将为你提供新的视角,让你对操作系统有更深刻的认识。
|
27天前
|
调度 开发者 Python
深入浅出操作系统:进程与线程的奥秘
【8月更文挑战第31天】 本文将带你探索操作系统中的核心概念——进程与线程。通过浅显易懂的语言和实际代码示例,我们将一起理解它们的定义、区别以及在操作系统中的作用。无论你是编程新手还是有一定经验的开发者,这篇文章都将为你打开一扇了解计算机内部工作原理的新窗口。
|
28天前
|
消息中间件 Unix Linux
深入浅出操作系统:进程与线程的奥秘
【8月更文挑战第31天】本文将带你一探操作系统中最为神秘的两个概念——进程和线程。我们将从基础的定义出发,逐步深入到它们在操作系统中的实现原理,并通过代码示例揭示它们在实际编程中的应用。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和理解。
|
28天前
|
开发者 Python
深入浅出操作系统:进程与线程的奥秘
【8月更文挑战第31天】在数字世界的幕后,操作系统扮演着至关重要的角色。本文将揭开进程与线程这两个核心概念的神秘面纱,通过生动的比喻和实际代码示例,带领读者理解它们的定义、区别以及如何在编程中运用这些知识来优化软件的性能。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和实用技巧。
|
28天前
探索操作系统中的线程同步机制
【8月更文挑战第31天】在多线程编程领域,理解并实现线程同步是至关重要的。本文通过浅显易懂的语言和生动的比喻,带你走进线程同步的世界,从互斥锁到信号量,再到条件变量,逐步揭示它们在协调线程行为中的作用。我们将一起动手实践,用代码示例加深对线程同步机制的理解和应用。

热门文章

最新文章