和每个进程都会有一个起标识作用的进程ID一样,每个线程也都会有一个线程ID。两者间要注意的地方就是,进程ID在整个系统中是唯一的;而由于一个线程只会属于一个进程,因此线程ID在整个系统中不要求唯一,但是在同一个进程中的线程ID必须是唯一的。
进程ID是用pid_t数据类型来表示,而线程ID用pthread_t数据类型来表示,而这两个数据类型实质就是一个整数。例如pthread_t类型的定义如下:
typedef unsigned long int pthread_t;
即线程ID本质上就是一个无符号长整型。
注:
在不同系统其类型的定义是有区别的,这一点在《UNIX环境高级编程》一书中有提到:
“ Linux 3.2.0使用无符号长整型表示pthread_t数据类型。Solaris 10把pthread_t数据类型表示为无符号整型。FreeBSD 8.0和Mac OS X 10.6.8用一个指向pthread结构的指针来表示pthread_t数据类型。”
如果一个线程要获取自己的线程ID(注意是自己获取自己的),可以调用pthread_self函数:
#include
pthread_t pthread_self(void);
在有线程机制的系统中,每一个进程都会至少有一个线程被称为主线程,这也是进程被创建时的第一个线程。下面可以查看一下主线程的线程ID:
#include
#include
int main(void)
{
printf("%ld\n", pthread_self()); \\ 线程ID在Linux下是无符号长整型,所以可以用%ld来格式化输出
return 0;
}
(自己编译运行看结果吧 … …)
创建线程可以调用pthread_create函数:
#include
int pthread_create (pthread_t *tidp,
const pthread_attr_t *attr,
void *(*start_rtn) (void *),
void *arg);
最初的线程被称为主线程这在前面讲过。按照套路后面被主线程创建的线程就可以叫做子线程了。
#include
#include
#include
#include
void* son_thread(void *arg)
{
for(int i = 0; i < 3; i++){
printf("I'm son_thread\n");
sleep(1);
}
}
int main(void)
{
pthread_t son_id;
pthread_create(&son_id, NULL, son_thread, NULL);
printf("son_thread has been created successfully.\n");
for(int i = 0; i < 5; i++){
printf("I'm parent_thread\n");
sleep(1);
}
exit(0);
}
注意pthread并不是Linux默认的库,所以编译时要加上-lpthread 。
上面程序的运行结果:
$ ./a.out
son_thread has been created successfully.
I'm parent_thread
I'm son_thread
I'm parent_thread
I'm son_thread
I'm parent_thread
I'm son_thread
I'm parent_thread
I'm parent_thread
$
注意程序中的sleep是必要的,因为子线程会随着主线程的关闭而强制关闭。如果没有sleep主线程很快就运行完了,子线程根本就没有机会运行。
终止线程的方法其实就在刚才提到了一种:随着主(父)线程的退出,子线程也会退出。线程的终止主要可以视为自愿终止和非自愿终止。
非自愿终止主要为通过同一进程中的其它线程来关闭某一线程。自愿终止一般为正常的退出并返回退出码、调用pthread_exit函数终止。
pthread_exit函数的原型:
#include
void pthread_exit(void *rval_ptr);
其中rval_ptr相当于一个线程的退出码。
某一个线程的rval_ptr也可以被其它线程获取到:
#include
int pthread_join(pthread_t thread, void **rval_ptr);
该函数就相当于wait调用,线程可以指定另一个线程thread的结束并获取到它的退出码,但是调用pthread_join的线程将会进入到阻塞态。
如果不需要退出码,可以将rval_ptr设为NULL。
有了上面两个函数,前面的代码即使没有sleep子线程也可以正常运行:
#include
#include
#include
#include
void* son_thread(void *arg)
{
for(int i = 0; i < 3; i++){
printf("I'm son_thread\n");
//sleep(1);
}
pthread_exit((void *)3);
}
int main(void)
{
pthread_t son_id;
void *rval_ptr;
pthread_create(&son_id, NULL, son_thread, NULL);
printf("son_thread has been created successfully.\n");
for(int i = 0; i < 5; i++){
printf("I'm parent_thread\n");
//sleep(1);
}
pthread_join(son_id, &rval_ptr);
printf("son_thread's exit code is %ld\n", (long)rval_ptr);
exit(0);
}
运行结果:
$ ./a.out
son_thread has been created successfully.
I'm parent_thread
I'm parent_thread
I'm parent_thread
I'm parent_thread
I'm parent_thread
I'm son_thread
I'm son_thread
I'm son_thread
son_thread's exit code is 3
$
pthread_exit看起来和正常退出没什么区别,但像之前的程序中主进程根本无法获知子线程退出状态,而使用pthread_exit就能做到了。