`
jishublog
  • 浏览: 868870 次
文章分类
社区版块
存档分类
最新评论

Linux线程同步之条件变量

 
阅读更多

条件变量变量也是出自POSIX线程标准,另一种线程同步机制,。主要用来等待某个条件的发生。可以用来同步同一进程中的各个线程。当然如果一个条件变量存放在多个进程共享的某个内存区中,那么还可以通过条件变量来进行进程间的同步。

每个条件变量总是和一个互斥量相关联,条件本身是由互斥量保护的,线程在改变条件状态之间必须要锁住互斥量。条件变量相对于互斥量最大的优点在于允许线程以无竞争的方式等待条件的发生。当一个线程获得互斥锁后,发现自己需要等待某个条件变为真,如果是这样,该线程就可以等待在某个条件上,这样就不需要通过轮询的方式来判断添加,大大节省了CPU时间。

在互斥量一文中说过互斥量是用于上锁,而不是用于等待现在这句话可以加强为:互斥量是用于上锁,条件变量用于等待

条件变量声明为pthread_cond_t数据类型,在<bits/pthreadtypes.h>中有具体的定义。

1条件变量初始化和销毁

/* Initialize condition variable  */
int pthread_cond_init (pthread_cond_t *__restrict __cond,
                              __const pthread_condattr_t *__restrict __cond_attr) ;

/* Destroy condition variable */
int pthread_cond_destroy (pthread_cond_t *__cond);
上面两个函数分别由于条件变量的初始化和销毁。

和互斥量的初始化一样,如果条件变量是静态分配的,可以通过常量进行初始化,如下:

pthread_cond_t mlock = PTHREAD_COND_INITIALIZER;

也可以通过pthread_cond_init()进行初始化,对于动态分配的条件变量由于不能直接赋值进行初始化,就只能采用这种方式进行初始化。那么当不在需要使用条件变量时,需要调用pthread_cond_destroy()销毁该条件所占用的资源。

2条件变量的属性设置

/* 初始化条件变量属性对象  */
int pthread_condattr_init (pthread_condattr_t *__attr);

/* 销毁条件变量属性对象  */
int pthread_condattr_destroy (pthread_condattr_t *__attr);

/* 获取条件变量属性对象在进程间共享与否的标识  */
int pthread_condattr_getpshared (__const pthread_condattr_t * __restrict __attr,
                                        int *__restrict __pshared);

/* 设置条件变量属性对象,标识在进程间共享与否 */
int pthread_condattr_setpshared (pthread_condattr_t *__attr, int __pshared) ;

这个属性的设置和互斥量属性设置是一样的,具体使用可以参考互斥量的用法:互斥量的属性设置

3条件变量的使用

/*  等待条件变为真 */
 int pthread_cond_wait (pthread_cond_t *__restrict __cond,
                              pthread_mutex_t *__restrict __mutex);

 /* 限时等待条件为真 */
 int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
                                   pthread_mutex_t *__restrict __mutex,
                                   __const struct timespec *__restrict __abstime);

 /* 唤醒一个等待条件的线程.  */
 int pthread_cond_signal (pthread_cond_t *__cond);

/* 唤醒等待该条件的所有线程 */
 int pthread_cond_broadcast (pthread_cond_t *__cond);

1pthread_cond_wait()函数用于等待条件被触发。该函数传入两个参数,一个条件变量一个互斥量,函数将条件变量和互斥量进行关联,互斥量对该条件进行保护,传入的互斥量必须是已经锁住的。调用pthread_cond_wait()函数后,会原子的执行以下两个动作:

  • 将调用线程放到等待条件的线程列表上,即进入睡眠;
  • 对互斥量进行解锁;

由于这两个操作时原子操作,这样就关闭了条件检查和线程进入睡眠等待条件改变这两个操作之间的时间通道,这样就不会错过任何条件的变化。

pthread_cond_wait()返回后,互斥量会再次被锁住。

2pthread_cond_timedwait()函数和pthread_cond_wait()的工作方式相似,只是多了一个等待时间。等待时间的结构为structtimespec

struct timespec{
time_t  tv_sec    //Seconds.
long    tv_nsec   //Nanoseconds.
};

函数要求传入的时间值是一个绝对值,不是相对值,例如,想要等待3分钟,必须先获得当前时间,然后加上3分钟。

要想获得当前系统时间的timespec值,没有直接可调用的函数,需要通过调用gettimeofday函数获取timeval结构,然后转换成timespec结构,转换公式就是:

timeSpec.tv_sec = timeVal.tv_sec;
timeSpec.tv_nsec = timeVal.tv_usec * 1000;

所以要等待3分钟,timespec时间结构的获得应该如下所示:

struct timeval now;
struct timespec until;
gettimeofday(&now);//获得系统当前时间

//把时间从timeval结构转换成timespec结构
until.tv_sec = now.tv_sec;
until.tv_nsec = now.tv_usec * 1000;

//增加min
until.tv_sec += 3 * 60;

如果时间到后,条件还没有发生,那么会返回ETIMEDOUT错误。

从pthread_cond_wait()pthread_cond_timewait()成功返回时,线程需要重新计算条件,因为其他线程可能在运行过程中已经改变条件。

3pthread_cond_signal()&pthread_cond_broadcast()

这两个函数都是用于向等待条件的线程发送唤醒信号,pthread_cond_signal()函数只会唤醒等待该条件的某个线程,pthread_cond_broadcast()会广播条件状态的改变,以唤醒等待该条件的所有线程。例如多个线程只读共享资源,这是可以将它们都唤醒。

这里要注意的是:一定要在改变条件状态后,再给线程发送信号。

考虑条件变量信号单播发送和广播发送的一种候选方式是坚持使用广播发送。只有在等待者代码编写确切,只有一个等待者需要唤醒,且唤醒哪个线程无所谓,那么此时为这种情况使用单播,所以其他情况下都必须使用广播发送。

下面是一个测试代码,模拟同步问题中经典的生产者消费者问题。

#include <iostream>
#include <queue>
#include <cstdlib>

#include <unistd.h>
#include <pthread.h>

using namespace std;

//把共享数据和它们的同步变量集合到一个结构中,这往往是一个较好的编程技巧。
struct{
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    queue<int> product;
}sharedData = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};

void * produce(void *ptr)
{
    for (int i = 0; i < 10; ++i)
    {
        pthread_mutex_lock(&sharedData.mutex);

        sharedData.product.push(i);

        pthread_mutex_unlock(&sharedData.mutex);

        if (sharedData.product.size() == 1)
            pthread_cond_signal(&sharedData.cond);

        //sleep(1);
    }
}

void * consume(void *ptr)
{
    for (int i = 0; i < 10;)
    {
        pthread_mutex_lock(&sharedData.mutex);

        while(sharedData.product.empty())
            pthread_cond_wait(&sharedData.cond, &sharedData.mutex);

        ++i;
        cout<<"consume:"<<sharedData.product.front()<<endl;
        sharedData.product.pop();

        pthread_mutex_unlock(&sharedData.mutex);

        //sleep(1);
    }
}

int main()
{
    pthread_t tid1, tid2;

    pthread_create(&tid1, NULL, consume, NULL);
    pthread_create(&tid2, NULL, produce, NULL);

    void *retVal;

    pthread_join(tid1, &retVal);
    pthread_join(tid2, &retVal);

    return 0;
}

程序的运行结果如下所示:

consume:0
consume:1
consume:2
consume:3
consume:4
consume:5
consume:6
consume:7
consume:8
consume:9

Jun 25, 2013 @Library
分享到:
评论

相关推荐

    linux线程同步

    介绍了linux线程同步的所有方式,包括互斥、自旋、信号量、条件变量等技术

    Linux系统编程之线程同步

    所以,互斥锁实质上是操作系统提供的一把“建议锁”(又称“协同锁”),建议程序中有多线程访问共享资源的时候使用该机制。但,并没有强制限定。 因此,即使有了mutex,如果有线程不按规则来访问数据,依然会造成...

    Linux多线程同步方式

    一般而言,linux下同步方式主要有4种,原子锁,互斥量,读写锁和条件变量。下面一一介绍几种同步方式。  1. spinlock  1) 概念  spinlock是一种互斥结构,通过CPU提供的特殊的原子指令集合实现互斥地访问一个...

    Linux 线程间同步机制

    互斥锁是一个二元变量,其状态为开锁(允许0)和上锁(禁止1),将某个共享资源与某个特定互斥锁绑定后,对该共享资源的访问如下操作: (1)在访问该资源前,首先申请该互斥锁,如果该互斥处于开锁状态,则申请到该...

    linux下多线程扑克牌游戏框架

    创建了1个控制线程和4个玩家线程。控制线程负责游戏流程的控制,4个玩家线程负责各自的叫牌、出牌的计算。5个线程间利用多线程同步技术(互斥量、条件变量、信号)完整模拟了真实的游戏流程。

    浅谈Linux条件变量的使用

    Linux线程同步之间存在多种机制,条件变量是一种类似操作系统里提到的生产者-消费者算法的同步机制,允许线程以无竞争的方式等待特定条件的发生。 示例伪代码: void* Thread1(void){ while(线程运行条件成立){ ...

    linux中一个程序的两个线程的同步(c语言实现)

    两个线程共享变量a,一个负责加一,一个负责输出 通过信号灯的pv操作完成

    浅谈互斥锁为什么还要和条件变量配合使用

    一旦其他的某个线程改变了条件变量,他将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。 两个线程操作...

    linux线程间的同步与互斥知识点总结

    线程同步: 条件变量 为什么使用条件变量? 对临界资源的时序可控性,条件满足会通知其他等待操作临界资源的线程,类似信号。 场景:T-DAY展会排队参观/生产者消费者模型 条件变量是什么? 是一种同步机制,一个线程用于...

    Linux多线程编程(二)

    条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待条件变量的条件成立而挂起(此时不再占用cpu);另一个线程使条件成立(给出条件成立信号)。为了防止竞争,条件变量的使用...

    linux线程相关操作导图

    主要为linux线程导图,其中包含:线程基本知识,线程创建与终止,栈清理,线程的取消选项,想成同步,线程属性,线程同步属性,重入,互斥量,锁,条件变量,线程与信号,线程与fork,线程与I/O等。 如果需要导图源...

    线程间同步机制 读写锁通信机制 线程与信号

    互斥锁通信机制 条件变量通信机制 读写锁通信机制 线程与信号

    Linux下多线程间通信

    线程间无需特别的手段进行通信,因为线程间可以共享数据结构,也就是一个全局变量可以被两个线程同时使用。不过要注意的是线程间需要做好同步,一般用 mutex。pthread_mutex_lock 声明开始用互斥锁上锁,此后的代码...

    详解Linux多线程使用信号量同步

    线程的信号量与进程间通信中使用的信号量的概念是一样,它是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。如果一个程序中有多个线程试图改变一个信号量的值,系统将保证所有的操作都将...

    linux进程间通信同步与互斥

    测试环境:64位ubuntu 13LTS 功能说明:使用互斥锁+条件变量+共享内存的方式实现进程(或线程)间的通信示例

    LInux 平台下物联网网关(多线程实现),嵌入式项目

    应用于物联网嵌入式项目 包括参数数据解析、协议转换、Socket收发、Sqlite、Uart、Camera等操作&UI界面。 Linux物联网网关是基于Linux操作系统的嵌入式...互斥锁和条件变量用于确保线程之间的同步和互斥访问共享资源。

    Linux多线程服务端编程:使用muduo C++网络库

    《Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。...

    详解Linux多线程编程(不限Linux)

    还有多线程编程的一些细节问题,如线程之间怎样同步、互斥,这些东西将在本文中介绍。我在某QQ群里见到这样一道面试题: 是否熟悉POSIX多线程编程技术?如熟悉,编写程序完成如下功能: 1)有一int型全局变量g_Flag...

    linux 文件字数统计的多线程程序

    报告包括源程序、主要功能模块、程序流程图、心得、程序调试等等,编写一个程序构建两个子线程,分别对两个文件的字数进行统计,两个线程统计完后写入到子线程与父线程共享的一段内存(或变量)中,然后父线程从这一...

Global site tag (gtag.js) - Google Analytics