1. 线程ID
1. 1 pthread_self()
获得当前线程的线程ID
#include <pthread.h> pthread_t pthread_self(void);
1.2 pthread_equal()
比较两个线程ID是否相等
#include <pthread.h> int pthread_equal(pthread_t t1, pthread_t t2);
2. 创建线程
2.1 pthread_create()
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
thread: pthread_t类型指针,创建成功时会将线程ID存在该变量内
attr: 指向 pthread_attr_t 类型的缓冲区,pthread_attr_t 数据类型定义了线程的各种属性(线程栈地址,栈大小等等),如果传递NULL表示使用默认属性创建线程
start_routine: 线程函数
arg: 线程函数的参数,是一个指针
返回值: 成功返回0;失败会返回错误码。
- 编译时需要加上: -lpthread
3. 终止线程
线程的终止方式有三种:
- return
- 线程调用pthread_exit
- 其他线程调用pthread_cancel
3.1pthread_exit()
调用 pthread_exit()相当于在线程的 start 函数中执行 return 语句,不同之处在于,可在线程 start 函数所调用的任意函数中调用 pthread_exit()来终止线程。
#include <pthread.h> void pthread_exit(void *retval);
4. 回收线程
pthread_join()函数将会以阻塞的形式等待指定的线程终止,如果该线程已经终止,则 pthread_join()立刻返回。
pthread_join类似于进程的waitpid,但也有不同:
- 线程之间的关系是对等的,进程中的任意线程均可调用 pthread_join()函数来等待另一个线程的终止
- 不能以非阻塞的方式调用pthread_join
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);
5. 取消线程
5.1 pthread_cancel()
通过调用 pthread_cancel()库函数向一个指定的线程发送取消请求.
发出取消请求之后,函数 pthread_cancel()立即返回,不会等待目标线程的退出
#include <pthread.h> int pthread_cancel(pthread_t thread);
5.2 pthread_setcancelstate()
设置本线程是否响应其他线程发来的取消线程请求
#include <pthread.h> int pthread_setcancelstate(int state, int *oldstate);
state:
- PTHREAD_CANCEL_ENABLE:线程可以取消,这是新创建的线程取消性状态的默认值,所以新建线程以及主线程默认都是可以取消的。
- PTHREAD_CANCEL_DISABLE:线程不可被取消,如果此类线程接收到取消请求,则会将请求挂起,直至线程的取消性状态变为 PTHREAD_CANCEL_ENABLE。
5.3 pthread_setcanceltype()
如果线程的取消性状态为 PTHREAD_CANCEL_ENABLE,那么对取消请求的处理则取决于线程的取消性类型,该类型可以通过调用 pthread_setcanceltype()函数来设置,它的参数 type 指定了需要设置的类型,而线程之前的取消性类型则会保存在参数 oldtype 所指向的缓冲区中,如果对之前的类型不敢兴趣,Linux下允许将参数 oldtype 设置为 NULL.
#include <pthread.h> int pthread_setcanceltype(int type, int *oldstate);
- PTHREAD_CANCEL_DEFERRED:取消请求到来时,线程还是继续运行,取消请求被挂起,直到线程到达某个取消点为止,这是所有新建线程包括主线程默认的取消性类型。
- PTHREAD_CANCEL_ASYNCHRONOUS:可能会在任何时间点(也许是立即取消,但不一定)取消线程,这种取消性类型应用场景很少,不再介绍!
5.4 取消点
取消点其实就是一些函数(通过man 7 pthreads查看有哪些,搜索Cancellation关键字),因为线程的某些操作可能是不能退出的,一旦退出会造成程序的运行发送一些不可预料的事情,故出现了取消点这个概念。在线程接收到取消请求时(并且设置了可接受取消请求),没运行到取消点之前是不会退出的。
如果线程没有上述的这些取消点,但我们又想要他取消,就可以使用该函数,目的就是产生一个取消点
#include <pthread.h> void pthread_testcancel(void);
6. 分离线程
线程分离后能够自动回收线程资源,不需要我们写代码调用pthread_join去回收。该过程是不可逆的
一个线程即可将另一个线程分离,同时也可以将自己分离出去。
#include <pthread.h> int pthread_detach(pthread_t thread);
7. 注册线程清理处理函数
与进程终止处理函数类似,线程在退出时也可去执行处理函数。
一个线程可以注册多个清理函数,这些清理函数记录在栈中,每个线程都可以拥有一个清理函数栈,栈是一种先进后出的数据结构,也就是说它们的执行顺序与注册(添加)顺序相反,当执行完所有清理函数后,线程终止。
#include <pthread.h> void pthread_cleanup_push(void (*routine)(void *), void *arg); void pthread_cleanup_pop(int execute);
**注意:尽管上面我们将 pthread_cleanup_push()和 pthread_cleanup_pop()称之为函数,但它们是通过宏来实现,
可展开为分别由{和}所包裹的语句序列,push和pop必须成对出现,不然会报错, eg: **
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<string.h> #include<pthread.h> void cleanup(void *arg) { printf("cleanup: %s\n", (char *)arg); } void *thread_func(void *arg) { pthread_cleanup_push(cleanup, "第一次调用"); // { pthread_cleanup_push(cleanup, "第二次调用"); // { pthread_cleanup_push(cleanup, "第三次调用"); // { pthread_cleanup_pop(0); // } printf("~~~~~~~~~~~~~~~~~~~~\n"); sleep(2); pthread_exit((void *)0); //pthread_cleanup_pop(0); pthread_cleanup_pop(0); // } pthread_cleanup_pop(0); // } } int main() { int ret = 0; pthread_t t; void *tret; ret = pthread_create(&t, NULL, thread_func, NULL); if (ret) { fprintf(stderr, "pthread_create error: %s\n", strerror(ret)); exit(-1); } // 等待线程结束 ret = pthread_join(t, &tret); if (ret) { fprintf(stderr, "pthread_join error: %s\n", strerror(ret)); exit(-1); } printf("新线程终止, code=%ld\n", (long)tret); exit(0); }
8. 线程属性
前面所讲的pthread_create有个参数attr就是线程属性。
8.1 属性初始化
#include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); // 属性初始化 int pthread_attr_destroy(pthread_attr_t *attr); // 释放属性
8.2 线程栈属性
#include <pthread.h> int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize); int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr); int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr);
8.3 分离状态属性
可以在创建线程的时候就设定好分离线程,不需要后面调用pthread_detach来分离
#include <pthread.h> int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
detachstate:
- PTHREAD_CREATE_DETACHED:新建线程一开始运行便处于分离状态,以分离状态启动线程,无法被其它线程调用 pthread_join()回收,线程结束后由操作系统收回其所占用的资源;
- PTHREAD_CREATE_JOINABLE:这是 detachstate 线程属性的默认值,正常启动线程,可以被其它线程获取终止状态信息。