传统UNIX程序模型中每一个进程都只支持一个线程控制,在概念上,这与每个线程仅仅由一个线程组成的线程模型是一样的。采用pthreads以后,当一个程序运行的时候,系统也会启动一个单线程控制进程,当程序运行的时候,其行为与传统进程并没有什么明显区别,除非它创建了多线程控制,其他线程可以通过调用函数pthread_create来创建。
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start_rtn)(void *), void *restrict arg);
Returns: 0 if OK, error number on failure.
tidp指针指向的内存地址在函数pthread_create成功返回以后将被设置为新创建线程的线程ID,参数attr用于自定义线程属性,我们将在12.3中讲述线程属性,现在,我们只是向其传递NULL,以创建一个具有默认属性的线程。
新创建的线程会从地址start_rtn开始运行,该函数仅仅带有一个参数,arg,是一个无符号指针。如果你需要传递多余一个参数,那么你就需要将这些参数存储到一个结构中,并传递该结构的地址到arg.
当一个新线程被创建的时候,哪一个线程先运行并没有任何保证,新创建的线程拥有对于进程地址空间的访问权限,并且继承了调用线程的floating-point environment以及信号掩码,但是挂起状态的信号将会被清除。
注意,pthread的函数在出现错误的时候仅仅返回一个错误码,它们并不会像其他POSIX函数一样设置errno.每一个线程都有一个errno的copy的目的是保证使用errno函数的兼容性。with threads,it is cleaner to return the error code from the function,thereby restriciting the scope of the error to the function that caused it, instead of relying on some global state that is changed as a side of the function.
Example
虽然没有一个可移植的打印进程ID的方式,我们可以写一个小程序,来看一下线程是如何工作的。在图11.2中的程序创建了一个线程并且打印了新创建线程与原来线程的线程ID以及进程ID。
#include "apue.h"
#include <pthread.h>
pthread_t ntid;
void printids(const char *s)
{
pid_t pid;
pthread_t tid;
pid = getpid();
tid = pthread_self();
printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid,
(unsigned long)tid, (unsigned long)tid);
}
void *thr_fn(void *arg)
{
printids("new thread: ");
return ((void *)0);
}
int main(void)
{
int err;
err = pthread_create(&ntid, NULL, thr_fn, NULL);
if(err != 0)
{
err_exit(err, "cann‘t create thread");
}
printids("main thread: ");
sleep(1);
exit(0);
}
图11.2打印线程ID
该程序有两个特别之处,这对于处理主线程与新线程之间的竞态条件是必须的。我们将在本章后面学到更好的竞态条件处理方法。
- 主线程需要睡眠,如果主线程睡眠,主线程就会结束运行退出,因此在新进程开始运行之前,可能整个进程就已经终止了,这一行为与操作系统的线程实现以及调度算法有关。
- 新线程通过调用函数pthread_self函数来获取其自身线程ID,而不是通过从共享内存中读取或者是通过其线程运行函数的参数来获取,在上文中我们提到,pthread_create将会返回新创建线程的线程ID到第一个参数中(tidp),在我们的例子中,主线程将会存储子线程ID到ntid中,但是新线程并不能保证可以安全地使用,因为如果新线程在主线程之前开始运行,而主线程还没有从pthread_create函数中返回的时候,新线程就会看到一个未初始化的ntid实体,而不是正确的线程ID.
运行效果如下图所示:
[email protected]:~/UnixProgram/Chapter11$ ./11_2Exe
main thread: pid 2340 tid 3077592768 (0xb77056c0)
new thread: pid 2340 tid 3077589872 (0xb7704b70)
[email protected]:~/UnixProgram/Chapter11$
可以看到运行结果与预期一致,两个线程拥有相同的进程ID,但是却拥有不同的线程ID.