POSIX 线程编程指南(五)

 

条件变量

概述

  • 条件变量提供 了另一个线程同步的方法,当mutex通过控制存取数据来实现线程同步时,条件变量允许线程基于实际的变量值来实现线程同步。
  • 没有条件变量,开发者需要线程不停的轮询以查询条件是否满足,因为线程要不停的忙等,会消耗很多资源。条件变量就是不用轮询而能达到同样目的方法。
  • 条件变量总是与mutex锁联系在一起。
  • 下面是一个使用条件变量的代表性序列

Main Thread

o                                申明和初始化需要同步的全局数据/变量(比如:count

o                                申明和初始化一个条件变量对象

o                                申明和初始化相关联的mutex

o                                创建threads A B并运行它们

Thread A

o                                Do work up to the point where a certain condition must occur (such as "count" must reach a specified value)

o                                Lock associated mutex and check value of a global variable

o                                Call pthread_cond_wait() to perform a blocking wait for signal from Thread-B. Note that a call to pthread_cond_wait() automatically and atomically unlocks the associated mutex variable so that it can be used by Thread-B.

o                                When signalled, wake up. Mutex is automatically and atomically locked.

o                                Explicitly unlock mutex

o                                Continue

Thread B

o                                Do work

o                                Lock associated mutex

o                                Change the value of the global variable that Thread-A is waiting upon.

o                                Check value of the global Thread-A wait variable. If it fulfills the desired condition, signal Thread-A.

o                                Unlock mutex.

o                                Continue

Main Thread

Join / Continue

 

条件变量

创建和销毁条件变量

函数:

pthread_cond_init (condition,attr)

pthread_cond_destroy (condition)

pthread_condattr_init (attr)

pthread_condattr_destroy (attr)

用法:

  • 条件变量必须用pthread_cond_t 类型来声明,而且在使用之前必须初始化。在这里有两种方法初始化条件变量:
    1. 静态的方式初始化,例如:
      pthread_cond_t myconvar = PTHREAD_COND_INITIALIZER;
    2. 动态的方式,利用pthread_cond_init()进行初始化,被创建的条件变量ID通过condition 参数返回给调用线程。这种方法允许条件变量设置属性attr
  • 可选的attr 用来设置条件变量属性,对于条件变量,这里仅仅只定义了一个属性:process-shared ,它允条件变量可以被其他进程中的线程访问。如果使用此条件变量属性,必须把它定义为pthread_condattr_t 类型(缺省的可以把它定义为NULL)。

注意,并不是所有的实现都提供process-shared 属性。

  • pthread_condattr_init() pthread_condattr_destroy() 函数用来创建和销毁条件变量属性对象。
  • pthread_cond_destroy() 用来释放不再需要的条件变量属性对象

 

条件变量

在条件变量中的等待与信号

函数:

pthread_cond_wait (condition,mutex)

pthread_cond_signal (condition)

pthread_cond_broadcast (condition)

用法:

  • pthread_cond_wait()会阻塞调用的线程,直到特定的条件(condition满足当这个线程运行时,mutex会被 加锁,当它阻塞时mutex会自动解锁。当收到信号唤醒线程时,mutex会被线程自动上锁 当线程完成更新共享数据后,开发者有责任解锁mutex
  • pthread_cond_signal()用来通知(唤醒)等待在条件变量上的另一线程,在mutex 被加锁后被调用,在完成pthread_cond_wait()运行后必须解锁mutex
  • 如果多于一个线程处于阻塞状态,应该用pthread_cond_broadcast()代替pthread_cond_signal()
  • 如果在调用pthread_cond_wait()前先调用pthread_cond_signal()就是逻辑错误。

当使用这些例程时,正确的加锁和解锁一个相关联的mutex是必须的,例如:

  • 在调用pthread_cond_wait()之前没有成功加锁mutex会导致线程不会阻塞
  • Failing to unlock the mutex after calling在调用 pthread_cond_signal()后没有成功解锁mutex,会导致pthread_cond_wait()一直运行 (保持线程阻塞).

Example: 使用条件变量

Example Code - Using Condition Variables

This simple example code demonstrates the use of several Pthread condition variable routines. The main routine creates three threads. Two of the threads perform work and update a "count" variable. The third thread waits until the count variable reaches a specified value.


#include <pthread.h>

#include <stdio.h>

 

#define NUM_THREADS  3

#define TCOUNT 10

#define COUNT_LIMIT 12

 

int     count = 0;

int     thread_ids[3] = {0,1,2};

pthread_mutex_t count_mutex;

pthread_cond_t count_threshold_cv;

 

void *inc_count(void *idp)

{

  int j,i;

  double result=0.0;

  int *my_id = idp;

 

  for (i=0; i<TCOUNT; i++) {

    pthread_mutex_lock(&count_mutex);

    count++;

 

    /*

    Check the value of count and signal waiting thread when condition is

    reached.  Note that this occurs while mutex is locked.

    */

    if (count == COUNT_LIMIT) {

      pthread_cond_signal(&count_threshold_cv);

      printf("inc_count(): thread %d, count = %d  Threshold reached./n",

             *my_id, count);

      }

    printf("inc_count(): thread %d, count = %d, unlocking mutex/n",

           *my_id, count);

    pthread_mutex_unlock(&count_mutex);

 

    /* Do some work so threads can alternate on mutex lock */

    for (j=0; j<1000; j++)

      result = result + (double)random();

    }

  pthread_exit(NULL);

}

 

void *watch_count(void *idp)

{

  int *my_id = idp;

 

  printf("Starting watch_count(): thread %d/n", *my_id);

 

  /*

  Lock mutex and wait for signal.  Note that the pthread_cond_wait

  routine will automatically and atomically unlock mutex while it waits.

  Also, note that if COUNT_LIMIT is reached before this routine is run by

  the waiting thread, the loop will be skipped to prevent pthread_cond_wait

  from never returning.

  */

  pthread_mutex_lock(&count_mutex);

  if (count<COUNT_LIMIT) {

    pthread_cond_wait(&count_threshold_cv, &count_mutex);

    printf("watch_count(): thread %d Condition signal

           received./n", *my_id);

    }

  pthread_mutex_unlock(&count_mutex);

  pthread_exit(NULL);

}

 

int main (int argc, char *argv[])

{

  int i, rc;

  pthread_t threads[3];

  pthread_attr_t attr;

 

  /* Initialize mutex and condition variable objects */

  pthread_mutex_init(&count_mutex, NULL);

  pthread_cond_init (&count_threshold_cv, NULL);

 

  /* For portability, explicitly create threads in a joinable state */

  pthread_attr_init(&attr);

  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

  pthread_create(&threads[0], &attr, inc_count, (void *)&thread_ids[0]);

  pthread_create(&threads[1], &attr, inc_count, (void *)&thread_ids[1]);

  pthread_create(&threads[2], &attr, watch_count, (void *)&thread_ids[2]);

 

  /* Wait for all threads to complete */

  for (i=0; i<NUM_THREADS; i++) {

    pthread_join(threads[i], NULL);

  }

  printf ("Main(): Waited on %d  threads. Done./n", NUM_THREADS);

 

  /* Clean up and exit */

  pthread_attr_destroy(&attr);

  pthread_mutex_destroy(&count_mutex);

  pthread_cond_destroy(&count_threshold_cv);

  pthread_exit(NULL);

 

}

―――――――――――――――――――――――――――――――――

输出

inc_count(): thread 0, count = 1, unlocking mutex
        
        
Starting watch_count(): thread 2
        
        
inc_count(): thread 1, count = 2, unlocking mutex
        
        
inc_count(): thread 0, count = 3, unlocking mutex
        
        
inc_count(): thread 1, count = 4, unlocking mutex
        
        
inc_count(): thread 0, count = 5, unlocking mutex
        
        
inc_count(): thread 0, count = 6, unlocking mutex
        
        
inc_count(): thread 1, count = 7, unlocking mutex
        
        
inc_count(): thread 0, count = 8, unlocking mutex
        
        
inc_count(): thread 1, count = 9, unlocking mutex
        
        
inc_count(): thread 0, count = 10, unlocking mutex
        
        
inc_count(): thread 1, count = 11, unlocking mutex
        
        
inc_count(): thread 0, count = 12  Threshold reached.
        
        
inc_count(): thread 0, count = 12, unlocking mutex
        
        
watch_count(): thread 2 Condition signal received.
        
        
inc_count(): thread 1, count = 13, unlocking mutex
        
        
inc_count(): thread 0, count = 14, unlocking mutex
        
        
inc_count(): thread 1, count = 15, unlocking mutex
        
        
inc_count(): thread 0, count = 16, unlocking mutex
        
        
inc_count(): thread 1, count = 17, unlocking mutex
        
        
inc_count(): thread 0, count = 18, unlocking mutex
        
        
inc_count(): thread 1, count = 19, unlocking mutex
        
        
inc_count(): thread 1, count = 20, unlocking mutex
        
        
Main(): Waited on 3  threads. Done.

 

LLNL 特定信息和建议

本节介绍了Livermore Computing's systems的特有细节。

应用:

  • 所有的LC production systems 包含了POSIX版本10(最后版本)的Pthreads实现,它是首选的实现。
  • 实现的区别在于能够创建的最大线程数目和它们的缺省栈大小。

编译:

 

Mixing MPI with Pthreads:

  • MPIPthreads都存在的环境中编程是一样的,在所有LC系统中很容易实现。
  • 设计:
    • 每个MPI进程典型的创建和管理N个线程,这里的N是能够最大限度发挥CPU/node 功能的线程数。
    • 最佳的N会随着你的平台和你的应用特征而变化。
    • 对于在节点间有通信适配层的IBM SP系统,在每个节点能够提供更加有效的2(或者多个)MPI任务.
    • 一般来说,如果多线程调用MPI可能会有问题,如果MPI调用需要在线程中调用,应该保证只在一个线程中。
  • 编译:
    • 针对平台和语言选择适当的MPI编译命令。
    • 注意要包含必须的标志 (如:-pthread or -qnosave)
    • MPICH不是线程安全的。
  • 下面是应用和Pthreads的示例:

 

没有提及的方面


有几个Pthreads API 的特征在本指南中没有提及,把它们列在下面:你可以从Pthread Library Routines Reference部分找到相应的信息。

  • Thread 调度
    • 调度会随着具体的实现而不同,在大多数时候,缺省的调度机制已经足够了。
    • Pthreads API提供的例程可以显式的设置调度策略和优先级,这些设置会覆盖缺省的设置。
    • API并不要求一定实现这些功能。
  • Keys: 线程特有数据
    • 随着线程从不同的例程调用和返回,本地数据会随之申请和销毁。
    • 为了保存栈数据,通常可以把它作为参数从一个例程传递到下一例程,或者把它保存到一个线程的全局变量中。
    • Pthreads 可能提供另外的方便且通用的方法去获取这些keys.
  • 为了处理优先级倒置("priority inversion")问题而需要的Mutex Protocol属性和Mutex 优先级管理
  • 条件变量的进程间共享
  • 注销线程
  • 线程和信号

 

Pthread 库例程参考

 

Pthread Functions

Thread Management

pthread_create

pthread_exit

pthread_join

pthread_once

pthread_kill

pthread_self

pthread_equal

pthread_yield

pthread_detach

Thread-Specific Data

pthread_key_create

pthread_key_delete

pthread_getspecific

pthread_setspecific

Thread Cancellation

pthread_cancel

pthread_cleanup_pop

pthread_cleanup_push

pthread_setcancelstate

pthread_getcancelstate

pthread_testcancel

Thread Scheduling

pthread_getschedparam

pthread_setschedparam

Signals

pthread_sigmask

Pthread Attribute Functions

Basic Management

pthread_attr_init

pthread_attr_destroy

Detachable or Joinable

pthread_attr_setdetachstate

pthread_attr_getdetachstate

Specifying Stack Information

pthread_attr_getstackaddr

pthread_attr_getstacksize

pthread_attr_setstackaddr

pthread_attr_setstacksize

Thread Scheduling Attributes

pthread_attr_getschedparam

pthread_attr_setschedparam

pthread_attr_getschedpolicy

pthread_attr_setschedpolicy

pthread_attr_setinheritsched

pthread_attr_getinheritsched

pthread_attr_setscope

pthread_attr_getscope

Mutex Functions

Mutex Management

pthread_mutex_init

pthread_mutex_destroy

pthread_mutex_lock

pthread_mutex_unlock

pthread_mutex_trylock

Priority Management

pthread_mutex_setprioceiling

pthread_mutex_getprioceiling

Mutex Attribute Functions

Basic Management

pthread_mutexattr_init

pthread_mutexattr_destroy

Sharing

pthread_mutexattr_getpshared

pthread_mutexattr_setpshared

Protocol Attributes

pthread_mutexattr_getprotocol

pthread_mutexattr_setprotocol

Priority Management

pthread_mutexattr_setprioceiling

pthread_mutexattr_getprioceiling

Condition Variable Functions

Basic Management

pthread_cond_init

pthread_cond_destroy

pthread_cond_signal

pthread_cond_broadcast

pthread_cond_wait

pthread_cond_timedwait

Condition Variable Attribute Functions

Basic Management

pthread_condattr_init

pthread_condattr_destroy

Sharing

pthread_condattr_getpshared

pthread_condattr_setpshared

下面是一些练习与其他信息

 

参考和更多的信息

 

  • Author: Blaise Barney, Livermore Computing.
  • "Pthreads Programming". B. Nichols et al. O'Reilly and Associates.
  • "Threads Primer". B. Lewis and D. Berg. Prentice Hall
  • "Programming With POSIX Threads". D. Butenhof. Addison Wesley
    www.awl.com/cseng/titles/0-201-63392-2
  • "Programming With Threads". S. Kleiman et al. Prentice Hall

    (完)

-------------------------------------

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值