什么是线程:
1>线程就是进程的一条执行路径,每一个进程都有一条线程,称之为主线程。
2>在Linux系统下:线程就是轻量级的进程;
3>在Linux内核看来,线程就是进程;因为linux系统早期是没有线程概念的,只有pcb表示的进程,而在后来发展中,程序员用进程的方式封装了线程,所以每个线程都有独立的pcb,所以在Linux内核中是分不清进程和线程的(一律当进程处理);
注意事项:
1>进程在创建线程之后,地址空间没有发生改变,但自己退化成一条线程(主线程);
2>同一进程创建出的子线程和主线程共用一个地址空间,但是每个线程都有自己独立的pcb,子线程的pcb是从主线程拷贝而来;
3>同一进程产生的所有线程 共享的是: 虚拟地址空间的.text段、.bass段、.data段、堆、动态库加载区、环境变量、命令行参数; 不共享的是:栈 。
4>地址空间的栈区会被n个线程平均分成n份,每个线程都独有自己的一份栈区。
线程与进程的区别:
1>进程是一个正在运行的程序,是系统进行资源分配的基本单位;
2>线程是进程内部的一条执行路径,是系统调度的基本单位;
3>线程的创建、管理等代价比进程小。
创建线程的函数原型:
int pthread_create(pthread_t* thread,const pthread_attr_t* attr,void*(*start_routine)(void*),void* arg);
引用头文件:#include
参数:
thread:传出参数,线程创建成功之后,会被设置一个合适的值;
attr:默认传NULL;
start_routine:函数指针,回调函数(指向子线程的处理函数);
arg:处理函数的参数
返回值:
==0: 创建成功
!=0: 创建失败,返回错误号,可以调用char* strerror(int errnum)函数打印错误信息。
例1、 创建五个线程,打印出他们是第几个被创建出来的
#include
#include
#include
//子线程的回调函数
void* show(void* arg)
{
//取参数
//int num = *((int*)arg);//取得是地址
int num = (int)arg;//取得是值
printf("第%d个创建的子线程:%lu\n",num,pthread_self());
return NULL;
}
int main()
{
int n = 1;
pthread_t pthid[5];//线程id
while (n < 6)
{
//第四个参数是给回调函数传的参数,传的是地址;在这个地方传地址会出现问题,因为五个进程的地址都指向同一块空间
//pthread_create(&pthid[n],NULL,show,(void*)&n);//创建线程
//给第四个参数传值
pthread_create(&pthid[n],NULL,show,(void*)n);//创建线程
n++;
}
printf("parent thread id:%lu\n",pthread_self());//打印主线程id
sleep(3);
return 0;
}
线程退出的函数原型: void pthread_exit(void* retval);
参数: retval指针:必须指向全局/堆的数据;
作用: 让此线程退出不影响其他线程。
例2、 使用pthread_exit函数,让子线程退出不影响主线程。
15 //子线程的回调函数
16 void* show(void* arg)
17 {
18 printf("child thread id:%lu\n",pthread_self());
19 int i = 0;
20 while(i < 5)
21 {
22 printf("chile i:%d\n",i++);
23 if(i == 2)
24 {
25 //exit(1);//会影响主线程
26 pthread_exit(NULL);
27 }
28 }
29 //pthread_exit(NULL);
30
31 return NULL;
32 }
33
34 int main()
35 {
36 pthread_t pthid;//线程id
37 int ret = pthread_create(&pthid,NULL,show,NULL);//创建线程
38 if(ret != 0)
39 {
40 printf("error num:%d,%s\n",ret,strerror(ret));//打印错误信息
41 }
42
43 printf("parent thread id:%lu\n",pthread_self());//打印主线程id
44
45 //pthread_exit(NULL);//退出主线程,此行之后的代码就不会被执行
46 int i = 1;
47 sleep(2);
48 while(i)
49 {
50 printf("parent i:%d\n",i++);
51 }
52
53 pthread_exit(NULL);
54 return 0;
55 }
线程回收的函数原型:
int pthread_join(pthread_t pthid,void** retval);
参数:
pthid:要回收的子线程id;
retval二级指针:读取线程退出时携带的状态信息;必须指向全局或堆上的内存是一个传出参数,指向的内存和pthread_exit参数指向同一块内存地址。
作用:
阻塞等待子线程退出,因为子线程和子进程一样,都无法回收自己的pcb。
例3、 使用pthread_join函数,让主线程阻塞等待回收子线程。
17 //子线程的回调函数
18 void* show(void* arg)
19 {
20 printf("child thread id:%lu\n",pthread_self());
21 int i = 0;
22 while(i < 5)
23 {
24 printf("child i:%d\n",i++);
30 }
32
33 return NULL;
34 }
35
36 int main()
37 {
38 pthread_t pthid;//线程id
39 int ret = pthread_create(&pthid,NULL,show,NULL);//创建线程
40 if(ret != 0)
41 {
42 printf("error num:%d,%s\n",ret,strerror(ret));//打印错误信息
43 }
44
45 printf("parent thread id:%lu\n",pthread_self());//打印主线程id
46
47 pthread_join(pthid,NULL);//如果想获取子线程的退出信息,则定义一个指针传入NULL的位置。
48 int i = 0;
49 while(i < 10)
50 {
51 printf("parent i:%d\n",i++);
52 }
53
54 //pthread_exit(NULL);
55 return 0;
56 }
线程分离的函数原型:
void pthread_detach(pthread_t pthid);
参数:
pthid:要和主线程分离的子线程id;
作用:
1>调用线程分离函数之后,子线程就可以自己回收自己的pcb;
2>调用线程分离函数后就不需要调用pthread_join()函数了。
例4、 使用pthread_detach函数,让主线程与子线程分离。
17 //子线程的回调函数
18 void* show(void* arg)
19 {
20 printf("child thread id:%lu\n",pthread_self());
21 int i = 0;
22 while(i < 5)
23 {
24 printf("chile i:%d\n",i++);
25 }
26 pthread_exit(NULL);
27
28 return NULL;
29 }
30
31 int main()
32 {
33 pthread_t pthid;//线程id
34 int ret = pthread_create(&pthid,NULL,show,NULL);//创建线程
35 if(ret != 0)
36 {
37 printf("error num:%d,%s\n",ret,strerror(ret));//打印错误信息
38 }
39
40 printf("parent thread id:%lu\n",pthread_self());//打印主线程id
41 pthread_detach(pthid);//将子线程与主线程分离
42
43 int i = 0;
44 while(i < 10)
45 {
46 printf("parent i:%d\n",i++);
47 }
48
49 pthread_exit(NULL);
50 return 0;
51 }
线程取消的函数原型:
void pthread_cancel(pthread_t pthid);
参数: pthid:对应子线程的线程id;
作用: 杀死对应线程id的子线程。
注意事项:
1>并不一定调用此函数就一定能杀死子线程;
2>将要被杀死的子线程的处理函数内部必须有一个取消点,即系统调用(间接调用也算,比如printf函数),才能成功杀死此子线程。
3>pthread_testcancle()函数:是一个系统调用,作为一个取消点存在,没有实际意义。
4>如果想让主线调用pthread_cancle杀死子线程,但是不知道子线程处理函数中没有使用系统调用,则子线程处理函数中调用pthread_testcancle()即可;
例5、 使用pthread_cancel函数,让主线程杀死子线程。
20 //子线程的回调函数
21 void* show(void* arg)
22 {
23 int i = 0;
24 while(i < 5)
25 {
26 if(i == 3)
27 {
28 printf("将要被杀死了,child_id=%lu,i=%d\n",pthread_self(),i);
29 }
30 i++;
31 }
32
33 return NULL;
34 }
35
36 int main()
37 {
38 pthread_t pthid;//线程id
39 int ret = pthread_create(&pthid,NULL,show,NULL);//创建线程
40 if(ret != 0)
41 {
42 printf("error num:%d,%s\n",ret,strerror(ret));//打印错误信息
43 }
44
45 pthread_cancel(pthid);//杀死子线程
46 printf("parent thread id:%lu\n",pthread_self());//打印主线程id
47
48 int i = 0;
49 while(i < 10)
50 {
51 printf("parent i:%d\n",i++);
52 }
53
54 pthread_exit(NULL);
55 return 0;
56 }
线程比较的函数原型:
void pthread_equal(pthread_t t1,pthread_t t2);
作用:
比较两个线程id是否相同
这是一个预留函数,我们现在的线程id都是无符号的长整型,直接数值比较就可以,没必要用这个函数。