线程
一个进程中的多个线程共享以下资源:
- 可执行的指令
- 静态数据
- 进程中打开的文件描述符
- 当前的工作目录
- 用户ID
- 用户组ID
每个线程中的私有资源包括:
- 线程ID(TID)
- PC(程序计数器)和相关寄存器
- 堆栈
- 错误号(errno)
- 优先级
- 执行状态和属性
创建线程
成功时返回0,失败时返回错误码,看的不太懂?直接上例子
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int *testThread(char *arg) {
printf("This is the thread test!\n");
return NULL;
}
int main() {
pthread_t tid;
int rel;
rel = pthread_create(&tid, NULL, (void*)testThread, NULL);
printf("This is the main thread!\n");
sleep(1);
return 0;
}
注意:主进程的退出,创建的线程也会退出,线程创建需要事件,如果主进程马上退出,那线程不能得到执行
线程结束
void pthread_exit(void *retval);
结束当前进程
retval可被其他线程通过pthread_join获取
线程私有资源被释放
线程查看TID
第一种方法:
pthread_t pthread_self(void)
第二种方法:
通过pthread_create函数中的第一个参数。
线程的参数传递
例如上个代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int *testThread(char *arg) {
printf("This is the thread test!\n");
return NULL;
}
int main() {
pthread_t tid;
int rel;
rel = pthread_create(&tid, NULL, (void*)testThread, NULL);
printf("This is the main thread!\n");
sleep(1);
return 0;
}
想将主函数的参数传递到testThread函数中,
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int *testThread(void *arg) {
printf("This is the thread test!\n");
// arg先强转为int*型,在前面加*解引用获取整数值
printf("input arg = %d\n", *(int*)arg);
return NULL;
}
int main() {
pthread_t tid;
int rel;
int arg = 5; // 想传递arg整形
// &arg变成int*arg类型,再强转变为void*arg
rel = pthread_create(&tid, NULL, (void*)testThread, (void*)&arg);
printf("This is the main thread!\n");
sleep(1);
return 0;
}
线程的回收
对于一个默认属性的线程来说,线程占用的资源并不会因为执行结束而得到释放,所以需要对线程进行回收
int pthread_join(pthread_t thread, void **retval);
- 成功时返回0,失败时返回错误码
- thread要回收的线程对象
- 调用线程阻塞直到thread结束
- *retval接收线程thread的返回值
pthread_join是阻塞函数,如果回收的线程没有结束,则会一直等待
例如代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *func(void *arg) {
printf("This is child thread!\n");
sleep(1);
pthread_exit("Thread return!");
}
int main() {
pthread_t tid;
void *retv;
// 先创建,再回收
pthread_create(&tid, NULL, func, NULL);
pthread_join(tid, &retv);
printf("Thread ret = %s\n",(char*)retv);
sleep(1);
}
则会先执行线程,等待线程结束后才会执行pthread_join
线程分离
方法一:
int pthread_detach(pthread_t thread);
成功时返回0,失败时返回错误号
指定该状态,线程主动与主控线程断开关系,线程结束后,不会产生僵尸进程
示例代码:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *func(void *arg) {
pthread_detach(pthread_self()); // 另一种方式
printf("This is child thread!\n");
sleep(1);
pthread_exit("Thread return!");
}
int main() {
pthread_t tid[5];
void *retv;
// 先创建,再回收
for(int i = 0; i < 5; i++) {
pthread_create(&tid[i], NULL, func, NULL);
// pthread_detach(tid); // detach实现线程回收
}
while(1) {
sleep(1);
}
}
方法二:
创建线程时设置为分离属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
线程的取消
int pthread_cancel(pthread_t threaed);
注意:线程的取消要有取消点,不是所有线程都能取消,线程的取消点主要是阻塞的系统调用
例如:
void *func(void *arg) {
printf("This is child thread\n");
while(1) {
sleep(1); // 取消点
}
pthread_exit("thread return");
}
其中的while循环就是取消点
如果没有取消点,可以自己定义一个取消点
void pthread_testcancel(void);
例如将上代码改为:
void *func(void *arg) {
printf("This is child thread\n");
while(1) {
pthread_testcancel(); // 取消点
}
pthread_exit("thread return");
}
如果遇到取消点但是不想取消,可以设置取消点的状态
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); // 不能被取消
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); // 可以被取消
例如将上个代码改为:
void *func(void *arg) {
printf("This is child thread\n");
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL); // 不能被取消
while(1) {
pthread_testcancel(); // 取消点
}
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL); // 可以被取消
while(1) {
sleep(1); // 取消点
}
pthread_exit("thread return");
}
第一个取消点不能被取消,第二个取消点可以被取消
线程的清理
在线程异常退出后,可以对线程的一些资源进行释放操作,打扫一下战场
void pthread_cleanup_push(void(*routine)(void*), void*arg);
void pthread_cleanup_pop(int execute);
执行条件:
被pthread_cancel取消
执行pthread_exit
非0参数执行pthread_cleanup_pop()
互斥锁
互斥锁的引入目的:当多个进程或线程访问同一个共享资源时,能确保只有一个进程或线程能访问该资源
动态方式创建:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexaddr_t *restrict attr);
静态方式创建:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
锁的销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
使用互斥锁步骤:
申请锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex)
成功时返回0,失败时返回错误码
mutex指向要初始化的互斥锁对象
pthread_mutex_lock如果无法获得锁,会阻塞
pthread_mutex_trylock如果无法获得锁,返回EBUSY,不会阻塞
读写锁
同一时刻只有一个线程可以获得写锁,但是同一时刻有多个线程可以获得读锁
当读写锁处于写锁状态,所有试图对读写锁读或者写的线程,都会被阻塞
当读写锁处于读锁状态,有写者试图加写锁时,之后的其他线程的读锁请求会被阻塞,避免长时间的不写锁
死锁的避免
- 锁越少越好,最好只使用一把锁
- 调整好锁的顺序