线程ID
线程ID用于标识线程,类似于进程ID的数据类型pid_t,线程ID的数据类型为pthread_t,是一个非负整数,在/usr/include/bits/pthreadtypes.h中定义:
/* Thread identifiers. The structure of the attribute type is not
exposed on purpose. */
typedef unsigned long int pthread_t;
说明:本文用到的源码及函数原型均出自Debian 8.2.0。
在不同的操作系统上,pthread_t的实现方式不一样。Linux 3.2.0使用无符号长整数表示pthread_t数据类型,Solaris 10使用无符号整数表示pthread_t数据类型,FreeBSD 8.0和Mac OS X 10.6.8用一个指向pthread结构的指针表示pthread_t数据类型。基于这个原因,为了在不同操作系统移植程序引入以下函数对线程ID进行操作。
比较线程ID的函数:
#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
Compile and link with -pthread.
获取当前线程ID的函数:
#include <pthread.h>
pthread_t pthread_self(void);
Compile and link with -pthread.
引入pthread_equal和pthread_self的原因是不同操作系统,pthread_t类型的定义不一致,为了能够在这些系统间移植
创建线程
创建线程的函数原型:
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
函数返回值:成功,返回0;否则,返回错误编号。
当pthread_create成功返回时,新创建线程的线程ID被存放到thread指向的内存单元。attr用于指定线程属性,将其置为NULL表示创建一个具有默认属性的线程。
新创建的线程从start_routine函数的地址开始运行,该函数只有一个无类型指针arg作为参数。当需要向start_routine传递一个以上的参数时,可把这些参数放在一个结构中,然后向函数传入指向该结构的指针。
创建线程时必不能保证线程的运行顺序,即是新线程先运行还是调用线程先运行。
退出线程
单个线程的3种退出方式:
- 从启动例程返回,返回值是线程的退出码;
- 被同一进程中的其他线程取消;
- 调用pthread_exit。
pthread_exit函数原型:
#include <pthread.h>
void pthread_exit(void *retval);
Compile and link with -pthread.
retval是一个无类型指针,包含线程返回码,该值适用于在同一进程中的其他线程调用pthread_join(3)。
pthread_join函数原型:
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Compile and link with -pthread.
调用pthread_join的线程将一直阻塞,直到指定的线程从启动例程、调用pthread_exit返回或者被取消。如果线程是从启动例程返回,retval就包含返回码;如果线程被取消,retval指向的内存单元就被设置为PTHREAD_CANCELED。
例1:获取线程退出码
#include <stdio.h>
#include <pthread.h>
void *thread1(void *arg)
{
printf("thread1 returning\n");
return ((void *)0);
}
void *thread2(void *arg)
{
printf("thread2 exiting\n");
pthread_exit((void *)1);
}
int main()
{
void *ret;
pthread_t tid1, tid2;
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_join(tid1, &ret);
printf("thread1 exit code: %ld\n", ret);
pthread_join(tid2, &ret);
printf("thread2 exit code: %ld\n", ret);
return 0;
}
程序运行执行结果:
thread2 exiting
thread1 returning
thread1 exit code: 0
thread2 exit code: 1
取消线程
可以调用pthread_cancel函数请求取消同一进程中的其他线程。
pthread_cancel函数原型:
#include <pthread.h>
int pthread_cancel(pthread_t thread);
Compile and link with -pthread.
修改例1源码,添加pthread_cancel测试代码。
例2:取消线程
#include <stdio.h>
#include <pthread.h>
void *thread1(void *arg)
{
printf("thread1 returning\n");
return ((void *)0);
}
void *thread2(void *arg)
{
printf("thread2 exiting\n");
pthread_exit((void *)1);
}
void *thread3(void *arg)
{
int oldstate;
printf("thread3 been canceled\n");
while(1);
}
int main()
{
void *ret;
pthread_t tid1, tid2, tid3;
pthread_create(&tid1, NULL, thread1, NULL);
pthread_create(&tid2, NULL, thread2, NULL);
pthread_create(&tid3, NULL, thread3, NULL);
pthread_join(tid1, &ret);
printf("thread1 exit code: %ld\n", ret);
pthread_join(tid2, &ret);
printf("thread2 exit code: %ld\n", ret);
pthread_cancel(tid3);
pthread_join(tid3, &ret);
printf("thread3 exit code: %ld\n", ret);
return 0;
}
程序运行结果:
thread2 exiting
thread3 been canceling
thread1 returning
thread1 exit code: 0
thread2 exit code: 1
^C
从结果来看,线程tid3并未被取消,而是进入死循环。需要注意pthread_cancel函数只是发送取消请求给线程,至于目标线程怎样及何时对该请求作出反映依赖于控制该线程两个属性:取消状态及类型。
线程的取消状态可被启用(enabled,对新创建线程是默认的)或禁用(disabled),由pthread_setcancelstate(3)函数决定。如果线程禁用了取消请求,那么该请求将一直保留在队列中直到启用取消请求。如果取消请求被启用,则线程的取消类型决定取消动作何时发生。
线程的取消类型可以是异步(asynchronous)或延迟(deferred),由pthread_setcanceltype(3)函数决定。异步取消意味着线程可在任何时候被取消(通常立即取消,但是系统对此无法保证)。延迟取消意味着取消动作被延迟直到线程下次调用一个函数(取消点)时。
修改例2源码,在线程tid3的printf语句下添加:
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
程序运行结果:
thread2 exiting
thread3 been canceling
thread1 returning
thread1 exit code: 0
thread2 exit code: 1
thread3 exit code: -1
线程tid3被取消。