线程概念
在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”。这里的内部是指指向同一个地址空间。一切进程至少都有一个执行线程。
进程和线程
- 进程是系统分配资源竞争的基本单位。
- 线程是程序执行的最小单位。
线程共享进程数据,但也拥有自己的一部分数据:线程ID,一组寄存器,栈,errno,信号屏蔽字,调度优先级。
同一地址空间,因此Text Segment,Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程都可以访问到,除此之外,各线程还共享以下进程资源和环境:文件描述符表,代码和数据共享
- 每种信号的处理方式(Hander表共享)
- 当前工作目录
- 用户id和组id
区别与关联
- 线程比进程更细致(粒度细), 线程是在进程内部的执行流。
- Linux下没有真正的线程,都是用进程模拟线程的。Linux下的线程被成为轻量级进程(成本低)。
- 线程是调度的基本单位,每个线程都有独立的上下文,用来切换。有独立的栈结构,用来保存临时数据。
进程:创建PCB,创建资源(地址空间,页表,物理内存)。
线程的创建
int pthread_create(pthread_t pthread,const pthread_attr_t *attr,void (start_routine)(void),void *arg);
参数:thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动的参数
返回值:成功返回0,失败返回错误码
代码:
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
void* rout(void* arg){
int i;
for( ; ; ){
printf("I am thread 1\n");
sleep(1);
}
}
int main(void){
pthread_t tid;
int ret;
if((ret = pthread_create(&tid,NULL,rout,NULL)) != 0){
fprintf(stderr,"pthread_create: %s\n",strerror(ret));
exit(EXIT_FAILURE);
}
int i;
for(; ; ){
printf("I am main thread\n");
sleep(1);
//两个死循环,两个执行流,新线程执行最上面的循环,主线程执行另外一个循环
}
}
注意在写Makefile时要引入库
线程的等待
- 为什么会有线程等待?已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
- 创建新的进程不会复用刚才退出线程的地址空间。
原型:
int pthread_join(pthread_t thread,void **value_ptr);
参数:
thread:线程ID
value_ptr:它指向一个指针,后者指向线程的返回值。
返回值:
成功返回0,失败返回错误码
调用该函数的线程挂起等待,直到ID为thread的线程终止。thread线程以不同的方式终止,通过pthread_join 得到的终止状态是不同的,总结如下:
- 如果thread线程通过return返回,value_ptr所指的单元里存的是thread线程函数的返回值。
- 如果thread线程被别的线程调用pthread_cancel异常终止,value_ptr所指的单元里存放的是常数PTHREAD_CANCELED.
- 如果thread线程是自己调用pthreadexit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。
- 如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
void* thread1(void* arg){
printf("thread 1 returning ...\n");
int *p = (int*)malloc(sizeof(int));
*p = 1;
return (void*)p;
}
void* thread2(void* arg){
printf("thread 2 returning ...\n");
int *p = (int*)malloc(sizeof(int));
*p = 2;
pthread_exit((void*)p);
}
void* thread3(void* arg){
while(1){
printf("thread 3 returning ...\n");
sleep(1);
}
return NULL;
}
int main(void){
pthread_t tid;
void *ret;
//thread 1 return
pthread_create(&tid,NULL,thread1,NULL);
pthread_join(tid,&ret);
printf("thread return,thread id %x,return code:%d\n",tid,*(int*)ret);
free(ret);
//thread 2 exit
pthread_create(&tid,NULL,thread2,NULL);
pthread_join(tid,&ret);
printf("thread return,thread id %x,return code:%d\n",tid,*(int*)ret);
free(ret);
//thread 3 cancel by other
pthread_create(&tid,NULL,thread3,NULL);
sleep(3);
pthread_cancel(tid);
pthread_join(tid,&ret);
if(ret == PTHREAD_CANCELED){
printf("thread return,thread id %x,return code:PTHREAD_CANCELED\n",tid);
}else{
printf("thread return,thread id %x,return code:NULL\n",tid);
}
}
线程的分离与结合
- 默认情况下,新创建的线程是可结合的,线程退出后,需要对其进行等待操作,否则无法释放资源,从而造成系统泄露。
- 如果不关心线程的返回值,等待是一种负担,这个时候,我们可以告诉系统,当线程退出时,自动释放线程资源。
原型:
int pthread_detach(pthread_t thread);
可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离。
pthread_detach(pthread_self());
结合和分离是冲突的,一个线程不能即是结合的又是分离的。线程就算被分离,一旦出现异常,也会连累进程。
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
#include<string.h>
void* thread_run(void* arg){
pthread_detach(pthread_self());
printf("%s\n",(char*)arg);
return NULL;
}
int main(){
pthread_t tid;
if( pthread_create(&tid,NULL,thread_run,"thread1 run...") != 0){
printf("create thread error\n");
return 1;
}
int ret = 0;
sleep(1);
if( pthread_join(tid,NULL) == 0){
printf("pthread wait success\n");
ret = 0;
}else{
printf("pthread wait failed\n");
ret = 1;
}
return ret;
}