目录
线程概念
进程:独立地址空间,拥有PCB
线程:有独立PCB,但没有独立的地址空间(共享)
Linux下:线程是最小的执行单位,进程是最小的分配资源单位
ps -Lf 进程id ——>线程号Lwp(CPU标识线程身份)
三级映射:进程PCB ——>页目录(首地址位于PCB)——> 页表 ——> 物理页面 ——> 内存单元
线程共享资源:
1. 文件描述符表
2. 每种信号的处理方式
3. 当前工作目录
4. 用户ID和组ID
5. 内存地址空间(.text .data .rodataa .bsss heap 共享库) 全局变量
线程非共享资源:
1. 线程id
2. 处理器现场和栈指针(内核栈)
3. 独立的栈空间
4. errno变量
5. 信号屏蔽字
6. 调度优先级
线程优缺点:
线程控制原语
pthread_self函数
获取线程ID,其作用对应进程中getpid()函数
原型:pthread_t pthread_self(void); 返回值:成功0 失败无
pthread_t为线程ID,在 Linux下为无符号整数,是进程内部识别线程的标志(两个进程间的线程ID允许相同)
pthread_create函数
原型:int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*start_rountn)(void *), void *arg); 创建子线程。
参1:传出参数,表新创建的子线程 id
参2:线程属性。传NULL表使用默认属性。
参3:子线程回调函数。创建成功,ptherad_create函数返回时,该函数会被自动调用。
参4:参3的参数。没有的话,传NULL
返回值:成功0 失败errno
示例代码,创建线程:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
void sys_err(const char *str){
perror(str);
exit(1);
}
void *tfn(void *arg){
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[]){
pthread_t tid;
printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());
int ret = pthread_create(&tid, NULL, tfn, NULL);
if(ret!=0){
perror("pthread_create error");
}
sleep(1);
return 0;
}
编译时需要在makefile中加上参数-lpthread,要注意在主线程执行完后地址会被销毁,因此要看到子线程的打印信息这里让主线程sleep一秒,结果如下:
示例,循环创建子线程,要注意传参使用值传递,借助强制类型转换(受参数类型限制):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
void sys_err(const char *str){
perror(str);
exit(1);
}
void *tfn(void *arg){
int i = (int) arg;
sleep(i);
printf("I'm %dth thread: pid = %d, tid = %lu\n", i+1, getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[]){
pthread_t tid;
int i,ret;
printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());
for(i=0;i<5;i++){
ret = pthread_create(&tid, NULL, tfn, (void*)i);
if(ret!=0){
sys_err("pthread_create error");
}
}
sleep(i);
return 0;
}
结果如下:
pthread_exit函数
原型:void pthread_exit(void *retval); 退出当前线程。
exit(); 退出当前进程。
return: 返回到调用者那里去。
pthread_exit(): 退出当前线程。
主线程退出不影响子线程
pthread_join函数
原型: int pthread_join(pthread_t thread, void **retval); 阻塞回收线程。
参数thread: 待回收的线程id
返回值:传出参数。 回收的那个线程的退出值。
线程异常借助,值为 -1。
返回值:成功0 失败errno
示例代码,使用pthread_join函数回收子线程:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
struct thrd{
int val;
char str[256];
};
void sys_err(const char *str){
perror(str);
exit(1);
}
void *tfn(void *arg){
struct thrd *tval;
tval = malloc(sizeof(tval));
tval->val = 100;
strcpy(tval->str, "hello thread");
return (void*)tval;
}
int main(int argc, char *argv[]){
pthread_t tid;
struct thrd *retval;
int ret = pthread_create(&tid, NULL, tfn, NULL);
if(ret!=0){
sys_err("pthread_create error");
}
ret = pthread_join(tid, (void **)&retval);
if(ret!=0){
sys_err("pthread_join error");
}
printf("child thread exit with var = %d, str = %s\n", retval->val, retval->str);
pthread_exit(NULL);
}
结果如下:
pthread_cancel函数
原型:int pthread_cancel(pthread_t thread); 杀死一个线程。需要线程进入内核(取消点)有系统调用。
thread: 待杀死的线程id
返回值:成功0 失败errno
如果子线程没有到达取消点,那么pthread_cancel 无效。我们可以在程序中使用 pthread_testcancel(),手动添加一个取消点。
成功被 pthread_cancel() 杀死的线程,返回 -1.使用pthead_join 回收。
示例代码,使用pthread_cancel杀死线程:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
struct thrd{
int val;
char str[256];
};
void sys_err(const char *str){
perror(str);
exit(1);
}
void *tfn(void *arg){
while(1){
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
sleep(1);
}
return NULL;
}
int main(int argc, char *argv[]){
pthread_t tid;
int ret = pthread_create(&tid, NULL, tfn, NULL);
if(ret!=0){
fprintf(stderr, "pthread_create error:%s\n", strerror(ret));
exit(1);
}
printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());
sleep(5);
ret = pthread_cancel(tid);
if(ret!=0){
fprintf(stderr, "pthread_cancel error:%s\n", strerror(ret));
exit(1);
}
while(1);
}
结果如下:
pthread_detach函数
原型:int pthread_detach(pthread_t thread); 设置线程分离,网络,多线程服务器常用
参数thread: 待分离的线程id
返回值:成功0 失败errno
示例代码,设置线程分离:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
void sys_err(const char *str){
perror(str);
exit(1);
}
void *tfn(void *arg){
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[]){
pthread_t tid;
int ret = pthread_create(&tid, NULL, tfn, NULL);
if(ret!=0){
fprintf(stderr, "pthread_create error:%s\n", strerror(ret));
exit(1);
}
ret = pthread_detach(tid);
if(ret!=0){
fprintf(stderr, "pthread_detach error:%s\n", strerror(ret));
exit(1);
}
ret = pthread_join(tid, NULL);
if(ret!=0){
fprintf(stderr, "pthread_join error:%s\n", strerror(ret));
}
printf("main: pid=%d, tid = %lu\n", getpid(), pthread_self());
pthread_exit((void*)0);
}
结果如下,线程分离后,系统会自动回收资源,用pthread_join去回收已经被系统回收的线程,那个线程号就是无效参数。
线程进程控制原语对照:
线程属性
线程属性结构体中主要成员:
1. int etachstate 线程的分离状态
2. size_t stacksize 线程的栈大小(默认平均分配)
3. size_t guardsize 线程栈警戒缓冲区大小(位于栈末尾)
属性不能直接设置,需要使用相关函数
线程属性初始化
注:应先初始化线程属性,再pthread_create创建线程,pthread_create应放在下面两函数之间
int pthread_attr_init(pthread_attr_t *attr); 成功0 失败errno 初始化线程属性
int pthread_attr_destroy(pthread_attr_t *attr);成功0 失败errno 销毁线程属性缩占用的资源
线程分离状态的函数:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); 设置线程属性 分离/非分离
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); 获取线程属性 分离/非分离
参数
attr:已初始化的线程属性 detachstate: PTHREAD_CREATE_DETACHED分离线程PTHREAD_CREATE_JOINABLE 非分离
示例代码,创建分离态的线程:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
void sys_err(const char *str){
perror(str);
exit(1);
}
void *tfn(void *arg){
printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());
return NULL;
}
int main(int argc, char *argv[]){
pthread_attr_t attr;
pthread_t tid;
int ret = pthread_attr_init(&attr);
if(ret!=0){
fprintf(stderr, "pthread_create error:%s\n", strerror(ret));
exit(1);
}
ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if(ret!=0){
fprintf(stderr, "attr_set error:%s\n", strerror(ret));
exit(1);
}
ret = pthread_create(&tid, &attr, tfn, NULL);
if(ret!=0){
fprintf(stderr, "pthread_create error:%s\n", strerror(ret));
exit(1);
}
ret = pthread_join(tid, NULL);
if(ret!=0){
fprintf(stderr, "pthread_join error:%s\n", strerror(ret));
exit(1);
}
printf("main: pid=%d, tid = %lu\n", getpid(), pthread_self());
ret = pthread_attr_destroy(&attr);
if(ret!=0){
fprintf(stderr, "attr_destroy error:%s\n", strerror(ret));
exit(1);
}
pthread_exit((void*)0);
}
结果如下,说明分离的线程已经被系统回收
线程使用注意事项: