线程标识
如同进程有进程ID一样,每一个线程也有一个线程ID,进程ID在整个系统中是唯一的,但线程ID只有在其所属进程上下文中才有意义。
线程ID是用pthread_t数据类型来表示。(Linux线程ID是用无符号长整型表示)
#include <pthread.h>
int pthread_self()
返回值:返回调用者线程ID
int pthread_equal(pthread_t tid1, pthread_t tid2)
返回值:若相等,返回非0数值,否则,返回0
线程创建
线程创建时并不能确定是哪个线程会先运行,新创建的线程可以访问进程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的挂起信号集会被清除。
#include <pthread.h>
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict_attr,
void* (*start_rtn)(void *),
void *restrict_arg)
返回值:成功返回0,出错返回错误编号
传统的UNIX模型中,每个进程只有一个控制线程。程序开始运行时,它是以单个进程中的单个控制线程启动。
新创建的线程ID会被设置为tidp
指向的内存单元,attr
用于定制各种线程,新创建的线程从start_rtn
开始运行,arg
用于给start_rtn
传参数。
pthread
函数在调用失败时通常返回错误码,它并不像其他函数一样设置errno
的值。
代码示例:
#include <pthread.h>
#include <stdio.h>
void* thr_fn(void *arg)
{
char **str = (char **)arg;
printf("thread ID: %lu\n", pthread_self());
for (; *str != NULL; str++)
printf("%s\n", *str);
return NULL;
}
int main()
{
int err;
char *args[3] = {"hello", "world", NULL};
pthread_t tid;
err = pthread_create(&tid, NULL, thr_fn, (void *)args);
sleep(1);
}
这里有1个特别的地方需要注意,主线程那里sleep
,是因为没有sleep
,那么新线程可能还没有开始运行,主线程就已经结束退出。
线程终止
如果进程中的任意线程调用exit
,_Exit
,_exit
那么整个进程就会终止。同时发送到线程的信号也有可能终止掉进程。
单个线程可以通过以下3种方式退出:
- 线程可以简单地从启动例程中返回,返回值是线程退出码
- 线程可以被同一进程中其他线程取消
- 线程调用
pthread_exit()
#include <pthread.h>
void pthread_exit(void *rval_ptr);
rval_ptr
指针会记录线程退出码。
回收已终止线程资源
#include <pthread.h>
void pthread_join(pthread_t thread, void **rval_ptr);
返回值:成功返回0,出错返回错误编号
调用线程将一直阻塞,直到指定的线程调用pthread_exit
,从启动例程返回或者被取消。如果线程简单的从它的启动例程返回,那么rval_ptr
就包含返回码,如果线程被取消,由rval_ptr
指定的内存单元就设置为PTHREAD_CANCELED
。
- 代码示例:
#include <stdio.h>
#include <pthread.h>
void* thr_fn1()
{
printf("thread id: %lu\n", pthread_self());
printf("world\n");
return (void *)0;
}
void* thr_fn2()
{
printf("thread id: %lu\n", pthread_self());
printf("world\n");
pthread_exit((void *)2);
}
int main()
{
pthread_t tid1, tid2;
void* tret;
pthread_create(&tid1, NULL, thr_fn1, NULL);
pthread_create(&tid2, NULL, thr_fn2, NULL);
pthread_join(tid1, &tret);
printf("thread1 exit code: %d\n", (int)tret);
pthread_join(tid2, &tret);
printf("thread2 exit code: %d\n", (int)tret);
}
分离线程
线程是可结合(joinable)或者是分离(detached)的。如果对一个对一个分离的线程调用pthread_join
,调用会失败,返回EINVAL
。
一个可结合的线程能够被其他线程收回资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是没有被释放。
但是一个分离的线程是不能被其他线程回收或者杀死,它的存储器资源在它终止时由系统自动回收。
#include <pthread.h>
int pthread_detach(pthread_t tid)
返回值:成功返回0,出错返回错误编号
参考资料
[1]《UNIX环境高级编程》[美] W. Richard Stevens Stephen A. Rago
[2] 《深入理解计算机系统》[美] Randal E. Bryant David R. O'Hallaron
[3] 《现代操作系统》[荷] Andrew S. Tanenbaum