APUE Chapter 11 笔记:Unix下的线程(I)

/**
 *任意转载,但请注明出处和原作者。听Leo说多写技术文章容易被猪头公司看中……
 */


开始通过APUE Chapter 11学习Unix下的多线程开发。以我的理解,线程就是一种轻量级的进程,与主进程共享内存空间,resource,文件描述符等,但是有独立的栈空间。

要使用线程,必须包含POSIX的线程库,这是GNU C默认不会被连接的库,所以需要用-lpthread指明。程序中要#include ,位于/usr/include。

标准的线程函数是这样的:
void* thread_function(void*)
也就是说,thread_function的参数是void*类型。这样,为了给线程提交参数,可以使用任何类型的变量或者struct,之后转化成为void*传递给线程,再由线程强制转换成为需要的类型。作为C程序来说这样的确是无可厚非的,但是如果在C++的强类型系统下则似乎并不优雅。

为了创建一个线程,首先必须有一个线程的handle,在POSIX下,是pthread_t。根据APUE所说,它的类型不确定。在我的Debian Linux 2.6.24内核下,是unsigned long,符合APUE的描述。创建线程的函数是:
int pthread_create(pthread_t* restrict,const pthread_attr_t* restrict, void* (*thread_function)(void*),void*);
附注:BBL版本的APUE在第三个参数,也就是线程的函数上漏了一个*。
第一个参数是线程的handle,第二个参数是线程的属性,第三个参数是线程函数指针,最后一个参数是传递给线程的参数指针。若创建线程成功,则返回0,否则返回errno。

取得当前线程的handle的函数:
pthread_t pthread_self(void);
比较两个pthread_t的函数:
int pthread_equal(pthread_t,pthread_t);

线程自销毁的方法有三种:
1、线程自己用return 返回。
2、线程被其他线程干掉。
3、线程使用
void pthread_exit(void*);
自销毁。其中参数为返回的void*指针。APUE特别用例子提醒了我们,一定不要返回栈上的指针。这个很好理解,因为线程被销毁之后,栈也会被破坏。那么我们就返回堆上的东西好了。

为了得到线程的返回值,使用
int pthread_join(pthread_t,void**);
但是调用这个函数的时候要注意,pthread_join会一直等待pthread_t的线程返回,或者它被强制cancel。在后一种情况下,void**得到的是PTHREAD_CANCELLED。
pthread_join会让线程进入detach状态,这个似乎Chapter 11没有讲,可能要留待Chapter 12再研究。

杀死一个线程可以用
int pthread_cancel(pthread_t);
线程可以选择死法,留待Chapter 12讲解。

线程可以写一份遗嘱,以stack的形式保存:
void pthread_cleanup_push(void (*proc)(void*),void*);
void pthread_cleanup_pop(int execute);
用以在线程结束之后做一些清理工作。对于第二个函数,如果execute==0,则仅仅弹出top位置的函数,否则,pthread_cleanup_pop还要执行这个函数。两个函数必须成对出现,并且在同一级的bracket里面。
这里线程结束是指线程执行pthread_exit,或者线程对pthread_cancel进行回应,如果没有回应,就不会调用cleanup函数。如果用return,同样不会清理。可见用pthread_exit()是一个好的习惯。
我检查了gcc 4.2.2下的pthread_cleanup_push和pthread_cleanup_pop,是一个宏而不是一个函数。而且在C和C++不同条件下,代码还不相同。留待以后仔细分析。

为了取得线程的返回值,采用了pthread_join。但是前提是线程没有被detach,否则线程的内存空间会被释放。
int pthread_detach(pthread_t);
让我感到疑惑的是,既然线程的内存空间不被释放,那么栈空间也应该是完好的。这个问题留待以后解决。

下面是线程同步的问题。晚些时候继续讨论。我要干活。

你可能感兴趣的:(C/C++,UNIX)