MENU

Linux线程同步:条件变量(pthread_cond_t)

• 2017 年 06 月 21 日 • Linux开发

什么是条件变量

先看一下APUE第三版对于条件变量的说明:

Condition variables are another synchronization mechanism available to threads. These synchronization objects provide a place for threads to rendezvous. When used with mutexes, condition variables allow threads to wait in a race-free way for arbitrary conditions to occur.
The condition itself is protected by a mutex. A thread must first lock the mutex to change the condition state. Other threads will not notice the change until they acquire the mutex, because the mutex must be locked to be able to evaluate the condition.

条件变量是利用线程间共享的全局变量进行同步的另一种机制,主要包括两个动作:

  • 一个线程等待"条件变量的条件成立"而挂起;
  • 另一个线程使"条件成立"(给出条件成立信号)。

条件变量由互斥量保护。线程在改变条件之前必须先锁定互斥量。其他线程在获得互斥量之前不会察觉到这种改变,因为互斥量必须在锁定以后才能计算条件。

使用条件变量进行线程同步

初始化和销毁

条件变量和互斥锁一样,都有静态动态两种创建方式:
静态方式使用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)   

等待

#include <pthread.h>

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 tsptr);

调用phread_cond_wait()pthread_cond_timewait()(以下简称wait函数)可以使当前线程等待某一条件的发生。前者为无条件等待,后者可以指定等待时间,即为计时等待。

调用wait函数时,系统使调用线程进入等待状态后释放锁(所以我们必须先加锁后调用wait函数)。在这一步操作中的检查条件和进入等待是原子操作,所以线程不会错过条件的变化。当wait函数返回时,mutex会再次被加锁

其中pthread_cond_timewait中用到的timespec结构定义如下:

struct timespec
{
    __time_t tv_sec;        /* Seconds.  */
    __syscall_slong_t tv_nsec;  /* Nanoseconds.  */
};

需要注意的是,timespec是一个绝对时间,所以在使用前我们需要先取得当前时间,再加上等待时间。例如下面这样:

#include <sys/time.h>
#include <stdlib.h>

void maketimeout(struct timespec *tsp, long minutes)
{
    struct timeval now;
    /* get the current time */
    gettimeofday(&now, NULL);
    tsp->tv_sec = now.tv_sec;
    tsp->tv_nsec = now.tv_usec * 1000; /* usec to nsec */
    /* add the offset to get timeout value */
    tsp->tv_sec += minutes * 60;
}

如果时间到了还没有等到条件变化,函数会对mutex重新加锁并返回一个ETIMEOUT的错误。

当wait函数返回成功时,需要重新检查条件,因为条件有可能已经被其他线程修改。

通知

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

当条件满足时,可以用这两个函数用来通知其他线程。

pthread_cond_signal会唤醒至少一个等待的线程,而pthread_cond_broadcast会唤醒所有等待的线程。必须注意的是:我们必须在状态发生变化之后再发送信号给其他线程

标签: Linux, 线程, C, 同步
返回文章列表 文章二维码
本页链接的二维码
打赏二维码
添加新评论