QNX学习笔记4 更多的同步机制 More on synchronization

之前已经介绍过了

mutex;semaphore;barrier 这三种同步的机制

在系统中,还有以下四中同步机制:

readers/writer lock;sleepon lock;condition variable;additional Neutrino service

一、readers/writer lock (rwlock)

显然,一次只有一个线程能够对数据结构进行写操作。 任意数量的线程都可以读线程,但是需要注意的一点是,读取数据的时候不能同时对数据进行写操作。

rwlock 的初始化和销毁函数如下:

int
pthread_rwlock_init (pthread_rwlock_t *lock,
const pthread_rwlockattr_t *attr);

int
pthread_rwlock_destroy (pthread_rwlock_t *lock);

attr 可以赋值 NULL 表示默认参数。

你不可以使用未初始化的或已经销毁的rwlock。

使用rwlock的情况有以下两种基本模式:

a reader will want “non-exclusive” access

a writer will want “exclusive” access

对应的,可以使用以下四种函数:

int
pthread_rwlock_rdlock (pthread_rwlock_t *lock);
int
pthread_rwlock_tryrdlock (pthread_rwlock_t *lock);
int
pthread_rwlock_wrlock (pthread_rwlock_t *lock);
int
pthread_rwlock_trywrlock (pthread_rwlock_t *lock);

至于try版本,它解释了半天我也没看太懂,大概是当你block不成功的时候,用try版本就会强行block。

最后,上了锁执行完任务后就要解锁,别站着茅坑不拉屎,用下面这个函数:

int
pthread_rwlock_unlock (pthread_rwlock_t *lock);

其他线程在等待你解锁的时候,状态显示会是READY。

需要注意的是,mutex做不到rwlock的是 rwlock 可以保证多个reader进行读操作,mutex一旦执行就只有一个reader被允许了。而semaphore不可以区分rwlock的两种基本模式,使用semaphore就会出现多个reader和一个或者更多writer的糟糕情况。

二、sleepon lock

在多线程程序中,有一种情况是,一个thread会等待,直到发生某些事情时它才会继续运行。

相关函数如下:

int
pthread_sleepon_lock (void);
int
pthread_sleepon_unlock (void);
int
pthread_sleepon_broadcast (void *addr);
int
pthread_sleepon_signal (void *addr);
int
pthread_sleepon_wait (void *addr);

注意它们并不符合POSIX标准。

手册中给出的一个例子是:

One thread is a producer thread that’s getting data from some piece of hardware. The other thread is a consumer thread that’s doing some form of processing on the data that just arrived.

一个线程(producer)从硬件获取数据,另一个线程(consumer)处理获取的数据。

相应的函数如下:

consumer ()
{
	while (1) {
		pthread_sleepon_lock ();
		while (!data_ready) {
			pthread_sleepon_wait (&data_ready);
		}
		// process data
		data_ready = 0;
		pthread_sleepon_unlock ();
	}
}
producer ()
{
	while (1) {
		// wait for interrupt from hardware here...
		pthread_sleepon_lock ();
		data_ready = 1;
		pthread_sleepon_signal (&data_ready);
		pthread_sleepon_unlock ();
	}
}

感觉还蛮容易看懂的吧,反正signal和wait这两个函数挺巧妙的,感觉是属于线程之间的消息传递,也挺神奇的。

三、condition variable(condvar)

这个东西和前面的 sleepon lock 挺像的,它说实际上sleepon lock 是建立在 condvar 的基础之上的。

他俩的区别像手册中提到的,应该是 sleepon lock 中隐藏了 mutex 而 condvar 没有,所以你在用condvar的时候就需要使用 mutex ,这样可以更灵活。(俺也不知道为啥灵活了。。。)下面就是书中的一个例子,感觉也挺简单的,反正还是主线程挂掉了其他thread再牛逼也没用。

#include <stdio.h>
#include <pthread.h>
int data_ready = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;
void *
consumer (void *notused)
{
 printf ("In consumer thread...\n");
 while (1) {
  pthread_mutex_lock (&mutex);
  while (!data_ready) {
   pthread_cond_wait (&condvar, &mutex);
 }
 // process data
 printf ("consumer: got data from producer\n");
 data_ready = 0;
 pthread_cond_signal (&condvar);
 pthread_mutex_unlock (&mutex);
}
}
void *
producer (void *notused)
{
 printf ("In producer thread...\n");
 while (1) {
  // get data from hardware
  // we’ll simulate this with a sleep (1)
  sleep (2);
  printf ("producer: got data from h/w\n");
  pthread_mutex_lock (&mutex);
  while (data_ready) {
   pthread_cond_wait (&condvar, &mutex);
  }
  data_ready = 1;
  pthread_cond_signal (&condvar);
  pthread_mutex_unlock (&mutex);
 }
}
main ()
{
 printf ("Starting consumer/producer example...\n");
 // create the producer and consumer threads
 pthread_create (NULL, NULL, producer, NULL);
 pthread_create (NULL, NULL, consumer, NULL);
 // let the threads run for a bit
 sleep (5);
}

signal vs broadcast

既然condvar是sleepon lock 的爸爸,那么condvar也会有

pthread_cond_signal() & pthread_cond_broadcast()

这两个东西。

所以就有必要说明一下这俩的区别。

实际从字面意思也能多多少少体会到。

signal 比较专一,一次只能唤醒一个thread 如果是多线程程序中,优先级最高的被唤醒,如果有好多个优先级相同的thread,那么场面就会混乱。。不可控制。

broadcast 广播通知所有thread 该运行了,它可以唤醒所有等待的线程,下面是书中提到的使用broadcast的例子(部分代码)

#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex_xy = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv_xy = PTHREAD_COND_INITIALIZER;
int x, y;
int isprime (int);
thread1 ()
{
for (;;) {
pthread_mutex_lock (&mutex_xy);
while ((x > 7) && (y != 15)) {
pthread_cond_wait (&cv_xy, &mutex_xy);
}
// do something
pthread_mutex_unlock (&mutex_xy);
}
}
thread2 ()
{
for (;;) {
pthread_mutex_lock (&mutex_xy);
while (!isprime (x)) {
pthread_cond_wait (&cv_xy, &mutex_xy);
}
// do something
pthread_mutex_unlock (&mutex_xy);
}
}
thread3 ()
{
for (;;) {
pthread_mutex_lock (&mutex_xy);
while (x != y) {
pthread_cond_wait (&cv_xy, &mutex_xy);
}
// do something
pthread_mutex_unlock (&mutex_xy);
}
}

要注意到的是每个thread中while中的条件不一样。。至于为啥要用这个机制,感觉完全可以用c中的switch-case去替代。不过它把这些放在同步下面,可能Windows在这个方面没有qnx同步性好吧。

sleepon vs condvar

这部分我也没太看懂。。等看懂了再来补充吧。

四、additional Neutrino service

thread pools 线程池 还是比较形象的一个机制吧

可以控制一定数量的 thread 处于 blocking 状态;同时,还有一定数量的thread 处于 processing 状态

下面就是 thread pool 的相关函数和变量 以及使用它们要用到的头文件

#include <sys/dispatch.h>

thread_pool_t *
thread_pool_create (thread_pool_attr_t *attr,
                    unsigned flags);

int
thread_pool_destroy (thread_pool_t *pool);

int
thread_pool_start (void *pool);

int
thread_pool_limits (thread_pool_t *pool,
                    int lowater,
                    int hiwater,
                    int maximum,
                    int increment,
                    unsigned flags);

int
thread_pool_control (thread_pool_t *pool,
                     thread_pool_attr_t *attr,
                     uint16_t lower,
                     uint16_t upper,
                     unsigned flags);

用法基本上也很简单

先 thread_pool_create()

然后 thread_pool_start()

接着就可以了,一般来说你不会用到 thread_pool_destroy() 因为你的程序要永远运行下去

其中 thread_pool_limits() 是用于指定 thread pool 的属性 和 调整 attr 的参数 的函数,并且 thread_pool_control() 是它的一个简化版本

至于 attr 真是一个相当复杂的数据结构,具体情况如下:

typedef struct _thread_pool_attr {
    // thread pool functions and handle
    THREAD_POOL_HANDLE_T    *handle;

    THREAD_POOL_PARAM_T
        *(*block_func)(THREAD_POOL_PARAM_T *ctp);

    void
        (*unblock_func)(THREAD_POOL_PARAM_T *ctp);

    int
        (*handler_func)(THREAD_POOL_PARAM_T *ctp);

    THREAD_POOL_PARAM_T
        *(*context_alloc)(THREAD_POOL_HANDLE_T *handle);

    void
        (*context_free)(THREAD_POOL_PARAM_T *ctp);

    // thread pool parameters
    pthread_attr_t          *attr;
    unsigned short          lo_water;
    unsigned short          increment;
    unsigned short          hi_water;
    unsigned short          maximum;
} thread_pool_attr_t;

*attr 跟之前提到的thread的数据结构一致 有什么优先级 堆栈大小之类的

lo_water 最少的处于blocking状态的thread数量

increment 如果thread低于lo_water 那么将创建 increment 个thread

hi_water 最多的处于blocking状态的thread数量

maximum thread pool 线程池中存在最多的线程数(处于blocking和processing状态thread数量的总和)

那几个函数的作用和 ctp handle 数据传递可以用下面这段伪代码来解释一下:

 FOREVER DO
    IF (#threads < lo_water) THEN
        IF (#threads_total < maximum) THEN
            create new thread
            context = (*context_alloc) (handle);
        ENDIF
    ENDIF
    retval = (*block_func) (context);
    (*handler_func) (retval);
    IF (#threads > hi_water) THEN
        (*context_free) (context)
        kill thread
    ENDIF
DONE

可以看出的是并不是处于blocking状态的thread数量小于lo_water 函数就会创建新线程,它同时还要满足thread总数小于maximum这个条件

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值