在多线程程序中,stdio文件描述符是被各个线程共享的。如果多个线程需要进行stdio操作,那么stdio同步就是一个值得关注的问题了。为了实现stdio线程安全,flockfile和funlockfile调用就显得非常重要了。在很多的情况下,一些stdio操作以不被中断的顺序是非常重要的。
下面的程序就是一个多线程调用stdio操作的例子,每个线程都试图使用stdio实现命令行读写,但是输入输出设备是唯一的。我们可以把prompt_routine()中的一次输出操作(printf)和一次读操作(fgets)看做一次事务。根据事务的特性,每次事务都是应该原子性的完成。如果没有flockfile和funlockfile调用,就可能出现如下的状况:一号线程printf调用完成之后被挂起(这是非常有可能的,printf会产生一次系统调用,这很可能会导致一次新的进程调度),然后二号线程占用了CPU,它顺利地执行了一次printf和fgets。这样的效果就是非常混乱的,用户明明在和一号线程交互,却莫名其秒地冲出一个二号线程(大哥,你小三吧!)。
/*
* flock.c
*
* Demonstrate use of stdio file locking to generate an "atomic"
* prompting sequence. The write to stdout and the read from
* stdin cannot be separated.
*/
#include <pthread.h>
#include "errors.h"
/*
* This routine writes a prompt to stdout (passed as the thread's
* "arg"), and reads a response. All other I/O to stdin and stdout
* is prevented by the file locks until both prompt and fgets are
* complete.
*/
void *prompt_routine (void *arg)
{
char *prompt = (char*)arg;
char *string;
int len;
string = (char*)malloc (128);
if (string == NULL)
errno_abort ("Alloc string");
flockfile (stdin);
flockfile (stdout);
printf (prompt);
if (fgets (string, 128, stdin) == NULL)
string[0] = '\0';
else {
len = strlen (string);
if (len > 0 && string[len-1] == '\n')
string[len-1] = '\0';
}
funlockfile (stdout);
funlockfile (stdin);
return (void*)string;
}
int main (int argc, char *argv[])
{
pthread_t thread1, thread2, thread3;
char *string;
int status;
#ifdef sun
/*
* On Solaris 2.5, threads are not timesliced. To ensure
* that our threads can run concurrently, we need to
* increase the concurrency level.
*/
DPRINTF (("Setting concurrency level to 4\n"));
thr_setconcurrency (4);
#endif
status = pthread_create (
&thread1, NULL, prompt_routine, "Thread 1> ");
if (status != 0)
err_abort (status, "Create thread");
status = pthread_create (
&thread2, NULL, prompt_routine, "Thread 2> ");
if (status != 0)
err_abort (status, "Create thread");
status = pthread_create (
&thread3, NULL, prompt_routine, "Thread 3> ");
if (status != 0)
err_abort (status, "Create thread");
status = pthread_join (thread1, (void**)&string);
if (status != 0)
err_abort (status, "Join thread");
printf ("Thread 1: \"%s\"\n", string);
free (string);
status = pthread_join (thread2, (void**)&string);
if (status != 0)
err_abort (status, "Join thread");
printf ("Thread 1: \"%s\"\n", string);
free (string);
status = pthread_join (thread3, (void**)&string);
if (status != 0)
err_abort (status, "Join thread");
printf ("Thread 1: \"%s\"\n", string);
free (string);
return 0;
}
问题:为什么各个线程之间不会形成死锁?在第三章中有过明确的说明,多临界资源情况下避免死锁的两种常见方法:
1.各个线程以固定的顺序对资源加锁
2.试加锁和退回
在本例中,各个线程运行的代码都是prompt_routine(),相当于使用了第一种方法,所以不会形成死锁。