1.导论:thread 是什么?为什么要用 thread?
thread 通常被称做轻量级的行程(lightweight process;lwp),这个说法似乎过于简单了一些,但却不失为一个好的起点; thread 是 unix process 的近亲,但却不完全相同。为了说明何谓 thread ,我们必须先了解 unix process 与 mach task、 thread 间的关系。在 unix 中,一个 process 包括了一个执行中的程序,和一些他所需的系统资源,诸如档案描述表和地址空间等。但是在 mach 中,一个 task 却只包括了一些系统资源; 而由thread 掌握了所有的执行活动。一个 mach task 可能有任意多个 thread , 而mach 系统中所有的 thread 均属于一些 task。属于同一个task 的所有 thread 共享该 task 所拥有的系统资源。因此, thread 实质上就是一个程序计数器、一个堆栈再加上一组缓存器。 unix 的一个 process 可以看成是一个只有一个 thread 的 mach task。
跟unix process 比起来, thread 是非常娇小玲珑的,因此对 cpu 而言,产生一个 thread 是一件相对便宜的工作。另一方面,共享系统资源的 thread 跟独占系统资源的 process 比起来,thread 是也是相当节省内存的。 mach thread 让程序设计师们能很方便的做出执行于单一或多重处理器环境下同时执行的程序。不需要考虑处理器多寡的问题,而直接得到多重处理的效能(如果有多的处理器的话)。此外即使在单一 cpu 的环境下, 如果程序是属于常常『休息』的那种,如 file 及 socket i/o,thread 还是能提供效能上的增进。
以下将介绍一些简单的 posix thread ,和他在 dec osf/1 os, v3.0.上的版本(译注:我是在 solaris 2.5.1 /和 sunos 4.1.4上测试的啦!差不多。),posix thread 简称为pthread,他和 non-posix 的 cthread非常相近。
2.Hello World
废话少说,现在就开始吧! pthread_create 函数建立一个 thread 。他需要四个
参数: thread 变量、 thread 特性、一个描述 thread 行为的函数和这个函数所需的
参数。举例如下:
pthread_t a_thread;
pthread_attr_t a_thread_attribute;
void thread_function(void *argument);
char *some_argument;
pthread_create( &a_thread, a_thread_attribute, (void *)&thread_function,
(void *) &some_argument);
thread attribute 描述 thread 的一些特性,目前我们只需要用他来指定 thread至少需要多大的堆栈。在未来会有许多有趣的 thread attribute ,但就目前而言,大部分的程序只需简单的指定 pthread_attr_default 就可以了。不像 process ,需要使用 fork() system call 让 child process 和他的 parents 同时开始执行, thread从 pthread_create 中指定的 thread_function 开始执行。理由非常简单:如果thread 不从一个另外的地方开始执行,将会造成一堆thread 使用相同的系统资源执行相同的指令。记得吗? thread 是『共享』系统资源的。(译注:在这里停下来,回忆一下 process 是怎么产生的... ^_^)
在知道如何产生一个 thread 后,就可以开始我们的第一个 thread 程序了!来设计一个 multi-thread 版的 printf("hello world\n"); 吧!一开始,我们需要两个thread 变量,和一个 thread function ,另外,还要能告诉不同的 thread 印出不同的讯息。我想要让不同的 thread 印出 "hello world" ,不同的两个部分 "hello" 和"world"。程序看起来像这样:
void print_message_function( void *ptr );
main()
{
pthread_t thread1, thread2;
char *message1 = "hello";
char *message2 = "world";
pthread_create( &thread1, pthread_attr_default,
(void*)&print_message_function, (void*) message1);
pthread_create(&thread2, pthread_attr_default,
(void*)&print_message_function, (void*) message2);
exit(0);
}
void print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s ", message);
}
注意 pthread_create 的参数 print_message_function 和他的参数 message1、message2,这支程序用 pthread_create 产生第一个 thread ,并以 "hello" 作为其起始参数;接下来产生第二个 thread ,指定其起始参数为 "world" 。第一个 thread开始激活的时候,从 print_message_function 开始执行,其传入参数为 "helllo"。他将 "hello" 印出来,然后结束。第二个 thread 则做差不多的事情:印出 "world"。看起来很合理,但是这个程序有两个主要的缺陷。
第一个缺点,由于两个 thread 是同时进行的,所以我们无法保证第一个thread会先执行到 printf 那一行,所以在屏幕上可能会看到 "hello world" ,也有可能会看到"world hello"。另外,在 main(parent thread)里的 exit 呼叫将结束整个process ,这将导致所有的 thread 一起结束。所以说,如果 exit 在 printf 前被执行的话,将不会有任何的输出产生。事实上,在任何一个 thread (不论 parent or child)里呼叫 exit 都将导致 process 结束,而所有的 thread 也跟着一起结束了。所以如果要结束一个 thread ,我们必须使用 pthread_exit 这个函数。
在我们小程序里有两个竞争条件(race condition),一、看看是 parent process先执行到 exit 呢?还是 child process 先执行到 printf ?二、还有两个child thread 到底是谁会先印出讯息呢?为了让程序按照我们希望的顺序运作,我们尝试强迫每个 thread 间相互的等待,下面这个程序加入了两个 sleep 达成这个目的。
void print_message_function( void *ptr );
main()
{
pthread_t thread1, thread2;
char *message1 = "hello";
char *message2 = "world";
pthread_create( &thread1, pthread_attr_default,
(void *) &print_message_function, (void *) message1);
sleep(10); //休息一下,等"hello"印出来再产生下一个 thread
pthread_create(&thread2, pthread_attr_default,
(void *) &print_message_function, (void *) message2);
sleep(10); //休息一下,等"world"印出来再结束。
exit(0);
}
void print_message_function( void *ptr )
{
char *message;