1. 线程的概念
1.1 什么是线程
轻量级进程,本质仍然是进程,都是通过clone函数实现的。
进程:独立的地址空间,拥有PCB,最小的资源分配单元
线程:没有独立的地址空间,拥有PCB,最小的执行单元
1.2 线程共享资源
- 文件描述符表
- 每种信号的处理方式
- 当前工作目录
- 用户ID和组ID
- 内存地址空间
1.3 线程非共享资源
- 线程id
- 处理现场和栈指针
- 独立的栈空间
- error变量
- 信号屏蔽字
- 调度优先级
1.4 线程优缺点
优点:提高并发性、占用资源小、通信方便
缺点:库函数不稳定、编写和调试困难、对信号支持不好
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);
功能:创建线程
函数参数:
- pthread_t *thread 线程ID,传出参数
- const pthread_attr_t *attr 线程的属性信息,一般用不到的
- void *(*start_routine) (void *) 线程函数,是一个void *func(void *)形式的函数指针
- void *arg 函数执行的参数
- 成功返回0,失败返回errno
注:编译需要添加pthread库
2.2 phtread_self函数
#include <pthread.h>
pthread_t pthread_self(void);
功能:返回调用函数的ID,这个值与调用pthread_create返回的pthread_t *thread一样。
2.3 pthread_exit函数
#include <pthread.h>
void pthread_exit(void *retval);
功能:终止调用的进程函数
函数参数:
- void *retval:指向的数据将作为线程退出时的返回值,主线程中的 pthread_join() 函数都可以接收到线程的返回值。如果线程不需要返回任何数据,将 retval 参数置为 NULL 即可。
pthread_exit和return的区别:
- pthread_exit() 可以自动调用线程清理程序(本质是一个由 pthread_cleanup_push() 指定的自定义函数),return 则不具备这个能力。
- 在主线程中, return 语句不仅会终止主线程执行,还会终止其它子线程执行。pthread_exit() 函数只会终止当前线程,不会影响进程中其它线程的执行。
2.4 pthread_join函数
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
功能:获取某个线程执行结束时返回的数据,pthread_join会一直阻塞调用它的线程,接收到目标线程的返回值,阻塞状态才会解除。
参数:
- thread代表创建的时候传出的第一个参数
- retval代表传出线程退出的信息
注:一个线程执行结束的返回值只能由一个 pthread_join() 函数获取,当有多个线程调用 pthread_join() 函数获取同一个线程的执行结果时,哪个线程最先执行 pthread_join() 函数,执行结果就由那个线程获得,其它线程的 pthread_join() 函数都将执行失败。
创建线程demo
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *thr(void *args)
{
printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
sleep(5);
return (void*)0;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, thr, nullptr);
printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
int ret = 0;
if ((ret = pthread_join(tid, nullptr)) > 0) { // 阻塞等待线程回收
printf("join err : %d, %s\n", ret, strerror(ret));
}
printf("I will exit.\n");
pthread_exit(nullptr);
return 0;
}
线程退出注意事项:
- 可以在线程中使用pthread_exit
- 可以在线程中使用return(主控线程return代表退出进程)
- exit代表退出整个进程
- pthread_exit或者return返回的指针所指的内存单元必须是全局的或者是在堆空间分配的,不能是在栈空间分配的。
- 主线程调用pthread_exit会阻塞等待其余线程都结束之后再退出。
2.5 pthread_detach函数
#include <pthread.h>
int pthread_detach(pthread_t thread);
功能:线程分离,线程主动与主线程断开关系,线程结束之后,其退出状态不由其他线程获取,而是自己直接自动释放,网络、多线程服务常用。pthread有两种状态joinable状态和unjoinable状态,一个线程默认的状态是joinable。如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当调用了pthread_join之后这些资源才会被释放。若是
unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。如果线程状态为 joinable,需要在之后适时调用pthread_join。
参数:
pthread_t thread:
注:不能对一个已经分离的线程调用pthread_join,这样的调用将返回EINVAL错误。也就是说,一个线程调用了pthread_detach就不能再调用pthread_join。
线程分离函数demo:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void *thr(void *args)
{
printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
sleep(5);
return (void*)0;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, thr, nullptr);
printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
pthread_detach(tid); // 线程分离
sleep(5);
int ret = 0;
if ((ret = pthread_join(tid, nullptr)) > 0) {
printf("join err : %d, %s\n", ret, strerror(ret));
}
printf("I will exit.\n");
pthread_exit(nullptr);
return 0;
}
pthread_join函数阻塞等待回收失败
Im main, pid = 525576, tid = 3930953536.
Im a thread, pid = 525576, tid = 3922568960.
join err : 22, Invalid argument
I will exit.
2.6 pthread_cancel函数
#include <pthread.h>
int pthread_cancel(pthread_t thread);
功能:仅仅是向目标线程发送 Cancel 信号,至于目标线程是否处理该信号以及何时结束执行,由目标线程决定。对于接收 Cancel 信号后结束执行的目标线程,等同于该线程自己执行pthread_exit(PTHREAD_CANCELED)。也就是说,当一个线程被强制终止执行时,它会返回 PTHREAD_CANCELED 。因此,当对一个cancel的进程进行pthrean_join回收时,得到的返回值为-1
参数:
- pthread_t thread:用于指定发送 Cancel 信号的目标线程。
杀死线程函数demo:
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void *thr(void *args)
{
while(1) {
printf("Im a thread, pid = %d, tid = %u.\n", getpid(), pthread_self());
sleep(1);
}
return (void*)0;
}
int main()
{
pthread_t tid;
pthread_create(&tid, nullptr, thr, nullptr);
printf("Im main, pid = %d, tid = %u.\n", getpid(), pthread_self());
sleep(5);
pthread_cancel(tid);
void *ret;
ret = pthread_join(tid, &ret);
printf("thread exit with : %d\n", (int)ret);
return 0;
}
2.7 进程属性控制函数
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr); // 初始化线程属性
int pthread_attr_destroy(pthread_attr_t *attr); // 销毁线程属性
// 设置属性分离态
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
功能: 设置属性分离状态
参数:
- attr init 初始化的属性
- detachstate
PTHREAD_CREATE_DETACHED 线程分离
PTHREAD_CREATE_JOINABLE 允许回收
设置线程属性demo
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
void *thr(void *arg)
{
printf("I am a thread.\n");
return NULL;
}
int main(int argc, char *argv[])
{
pthread_attr_t attr;
pthread_attr_init(&attr); // 初始化属性
pthread_attr_getdetachstate(&arr, PTHREAD_CREATE_DETACHED); //设置属性分离
pthread_t tid;
pthread_create(&tid, &attr, thr, NULL);
int ret = 0;
if ((ret = pthread_join(tid, NULL)) > 0) {
printf("join err: %d, %s\n", ret, strerror(ret));
}
pthread_attr_destroy(&attr);
}
其他:
- alias echomake = ‘cat ~/bin/makefile.template >> makefile’
- set -o vi 设置shell里vi的快捷键,设置后vi的快捷键可以在shell中使用
- 查看线程库版本:getconf GNU_LIBPTHREAD_VERSION