message = (char *) ptr;
printf("%s", message);
pthread_exit(0);
}
这个程序达成我们的目的了吗?不完全是,原因在于使用 timming delay 来达成thread 间的同步是错误的,因为 thread 间的紧密耦合(tightly coupled)特性很容易让我们使用一些不精确的方法来达成其间的同步处理;然而我们却不该这么做。在这个程序中我们遇到的竞争条件和分布式应用程序中,资源共享的情况完全相同。共享的资源为标准输出,而分散计算成原则为程序中的三个 thread。第一个thread必须在第二个thread 前使用 printf/stdout,而两者皆必须在 parent thread 呼叫exit 前完成他们的工作。
除了使用 delay 来达成同步的效果外,另一个错误发生在 sleep 系统呼叫;如同exit 对 process 的影响一样,当 thread 呼叫 sleep 时,讲导致整个 process 停下来。这表示所有属于这个 process 的 thread 也将跟着停顿下来。因此在上面这个程式中,呼叫 sleep 除了平白让程序慢了20秒,并不会有什么额外影响。另外一个适用的函数是 pthread_delay_np (np 表示 not process)。举例来说,要让thread 停顿两秒钟,可以用下列程序:
struct timespec delay;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_delay_np( &delay );
本节提到的函数有:pthread_create(),
pthread_exit(),
pthread_delay_np().
3.Thread 同步问题
posix 提供了两组用来使 thread 同步的基本指令: mutex 和 condition variable。mutex 指的是一组用来控制共享资源存取的一组函数。注意,在使用thread的情况下,因为整个地址空间都是共享的,所以所有的东西都可以视为共享资源。在一般情况下, thread 使用一些在pthreadcreate 之前定义或在其所呼叫的函数中定义的变量来完成其工作,并将他的成果经由整体变量合并。对这些大家都可以存取的变量,我们必须加以控制。
以下是一个 reader/writer 程序,程序中有一个reader,一个writer,他们共享
一个 buffer,且使用 mutex 来控制这个 buffer 的存取。
void reader_function(void);
void writer_function(void);
char buffer;
int buffer_has_item = 0;
pthread_mutex_t mutex;
struct timespec delay;
main()
{
pthread_t reader;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_mutex_init(&mutex, pthread_mutexattr_default);
pthread_create( &reader, pthread_attr_default, (void*)&reader_function,
null);
writer_function();
}
void writer_function(void)
{
while(1)
{
pthread_mutex_lock( &mutex );
if ( buffer_has_item == 0 )
{
buffer = make_new_item();
buffer_has_item = 1;
}
pthread_mutex_unlock( &mutex );
pthread_delay_np( &delay );
}
}
void reader_function(void)
{
while(1)
{
pthread_mutex_lock( &mutex );
if ( buffer_has_item == 1)
{
consume_item( buffer );
buffer_has_item = 0;
}
pthread_mutex_unlock( &mutex );
pthread_delay_np( &delay );
}
}
在这个简单的程序中,我们假设 buffer 的容量只有 1,因此这个 buffer 有两个可能的状态:『有一笔资料』或『没有资料』。 writer 首先将 mutex 锁定,如果 mutex 已经被锁定,则暂停,直到 mutex 被解锁。然后看看 buffer 是否是空的,若buffer 处于『没有资料』的状态,writer 产生一笔新的资料,将其放入 buffer中。然后将旗标buffer_has_item 设为 1,让 reader 可藉此旗标得知 buffer 内有一笔资料。最后writer 将 mutex 解锁,并休息 2 秒钟,让 reader 可藉此一空档取出 buffer 内的资料。这里使用的 delay跟之前的 delay 有截然不同的意义,如果不加上这个 delay 的话,writer 在 unlock mutex 后的下一个指令就是为了产生另一笔新的资料,再度 lock mutex。这将造成 reader 没有机会读取 buffer 中的资料。因此在此处加上一个 delay 看起来是个不错的主意。
reader 看起来和 writer 差不多,它首先 lock mutex,然后看看buffer 中是否有资料,若有资料则将其取出,然后将 mutex 解锁,接着 delay 2 秒,让 writer 有机会放入新的资料。在这个例子中,writer 和 reader 就这样一直的 run 下去,不断的产生/移除 buffer 中的资料。在其它的情况下,我们可能不再需要使用 mutex 了,此时可以使用 pthread_mutex_destroy(&mutex); 来释放 mutex。
在初始 mutex 的时候,我们使用了 pthread_mutexattr_default 来当作 mutex 特性。在 osf/1 中,mutex 特性没啥用处,所以这样设就够了。
mutex 一般用在解决 race condition 问题,但是 mutex 并不是一个很强的机制,因为他只有两个状态:locked 和 unlocked。posix 定义的条件变量(condition variable)将 mutex 的功能加以延伸,能够做到让某一个 thread 能暂停,并等待另一个 thread 的信号(signal)。当信号来了,thread 就醒过来,然后将相关的mutex lock 起来。这样的作法可以解决 reader/writer 程序中的 spin-lock 问题。附录 a 中有一个使用 mutex 和 condition variable 做成的一个简单的 integer semaphores。有关 condition variable 的详细用法可以参考 man page。
本节提到的函数有:pthread_mutex_init(),
pthread_mutex_lock(),