一、线程的创建【pthread_create()创建子线程】
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg );
功能:
创建一个线程。
参数:
thread:线程标识符地址。
attr:线程属性结构体地址,通常设置为 NULL。
start_routine:线程函数的入口地址(回调函数)。
arg:传给线程函数(回调函数)的参数。
返回值:
成功:0
失败:非 0
在一个线程中调用pthread_create()创建新的线程后:
- 当前线程从pthread_create()返回继续往下执行,
- 新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。
由于pthread_create的错误码不保存在errno中,因此不能直接用perror()打印错误信息,可以先用strerror()把错误码转换成错误信息再打印。
参考程序:
// 回调函数
void *thread_fun(void * arg)
{
sleep(1);
int num = *((int *)arg);
printf("int the new thread: num = %d\n", num);
return NULL;
}
int main()
{
pthread_t tid;
int test = 100;
// 返回错误号
int ret = pthread_create(&tid, NULL, thread_fun, (void *)&test);
if (ret != 0)
{
printf("error number: %d\n", ret);
// 根据错误号打印错误信息
printf("error information: %s\n", strerror(ret));
}
while (1);
return 0;
}
创建的新线程执行完毕后,线程资源会自动被PCB回收。???
1、父子线程关系
2、同一进程内的线程共享资源
2.1 数据段共享
2.2 堆区共享
三、线程资源回收【pthread_join()函数】
默认情况下,主线程结束后则默认该进程结束,子线程会直接挂掉。
如果线程没有被回收,则该线程会成为僵尸线程。
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
功能:
等待线程结束(此函数会阻塞),并回收线程资源,类似进程的 wait() 函数。如果线程已经结束,那么该函数会立即返回。
参数:
thread:被等待的线程号。
retval:用来存储线程退出状态的指针的地址。
返回值:
成功:0
失败:非 0
参考程序:
void *thead(void *arg)
{
static int num = 123; //静态变量
printf("after 2 seceonds, thread will return\n");
sleep(2);
return #
}
int main()
{
pthread_t tid;
int ret = 0;
void *value = NULL;
// 创建线程
pthread_create(&tid, NULL, thead, NULL);
// 等待线程号为 tid 的线程,如果此线程结束就回收其资源
// &value保存线程退出的返回值
pthread_join(tid, &value);
printf("value = %d\n", *((int *)value));
return 0;
}
调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程以不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:
1) 如果thread线程通过return返回,retval所指向的单元里存放的是thread线程函数的返回值。
2) 如果thread线程被别的线程调用pthread_cancel异常终止掉,retval所指向的单元里存放的是常数PTHREAD_CANCELED。
3) 如果thread线程是自己调用pthread_exit终止的,retval所指向的单元存放的是传给pthread_exit的参数。
四、线程分离【pthread_detach()函数】
一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。
不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL错误。也就是说,如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。
#include <pthread.h>
int pthread_detach(pthread_t thread);
功能:
使调用线程与当前进程分离,分离后不代表此线程不依赖与当前进程,线程分离的目的是将线程资源的回收工作交由系统自动来完成,也就是说当被分离的线程结束之后,系统会自动回收它的资源。所以,此函数不会阻塞。
参数:
thread:线程号。
返回值:
成功:0
失败:非0
五、线程退出【pthread_exit()函数】
在进程中我们可以调用exit函数或_exit函数来结束进程,在一个线程中我们可以通过以下三种在不终止整个进程的情况下停止它的控制流。
- 线程从执行函数中返回。
- 线程调用pthread_exit退出线程。
- 线程可以被同一进程中的其它线程取消。
#include <pthread.h>
void pthread_exit(void *retval);
功能:
退出调用线程。一个进程中的多个线程是共享该进程的数据段,因此,通常线程退出后所占用的资源并不会释放。
参数:
retval:存储线程退出状态的指针。
返回值:无
参考程序:
void *thread(void *arg)
{
static int num = 123; //静态变量
int i = 0;
while (1)
{
printf("I am runing\n");
sleep(1);
i++;
if (i == 3)
{
pthread_exit((void *)&num);
// return #
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int ret = 0;
pthread_t tid;
void *value = NULL;
pthread_create(&tid, NULL, thread, NULL);
pthread_join(tid, &value);
printf("value = %d\n", *(int *)value);
return 0;
}
六、线程取消【pthread_cancel()函数】
#include <pthread.h>
int pthread_cancel(pthread_t thread);
功能:
杀死(取消)线程
参数:
thread : 目标线程ID。
返回值:
成功:0
失败:出错编号
注意:线程的取消并不是实时的,而又一定的延时。需要等待线程到达某个取消点(检查点)。
类似于玩游戏存档,必须到达指定的场所(存档点,如:客栈、仓库、城里等)才能存储进度。
杀死线程也不是立刻就能完成,必须要到达取消点。
取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用creat,open,pause,close,read,write..... 执行命令man 7 pthreads可以查看具备这些取消点的系统调用列表。
可粗略认为一个系统调用(进入内核)即为一个取消点。
参考程序:
void *thread_cancel(void *arg)
{
while (1)
{
pthread_testcancel(); //设置取消点
}
return NULL;
}
int main()
{
pthread_t tid;
pthread_create(&tid, NULL, thread_cancel, NULL); //创建线程
sleep(3); //3秒后
pthread_cancel(tid); //取消tid线程
pthread_join(tid, NULL);
return 0;
}