一、线程的概念
说起线程,我们先说说进程。
1.1、什么是进程
每个进程都拥有自己的数据段、代码段和堆栈段,这就造成进程在进行创建(即:4G的虚拟内存)、切换、撤销操作时,需要较大的系统开销。为了减少系统开销,从进程中演化出了线程。
1.2、什么是线程
线程是进程中的独立控制流,由环境(包括寄存器组和程序计数器)和一系列的执行指令组成。线程存在于进程中,共享进程的资源。每个进程有一个地址空间和一个控制线程。
二、线程总结
1)一般把线程称之为轻量级的进程
2)一个进程可以创建多个线程,多个线程共享一个进程的资源
3)每一个进程创建的时候系统会给其4G虚拟内存,3G用户空间是私有的,所以进程切换时,用户空间也会切换,所以会增加系统开销,而一个进程中的多个线程共享一个进程的资源,所以线程切换时不用切换这些资源,效率会更高。
4)线程的调度机制跟进程是一样的,多个线程来回切换运行。
三、多线程
3.1、使用多线程的目的主要有以下几点:
(1) 多任务程序的设计
一个程序可能要处理不同应用,要处理多种任务,如果开发不同的进程来处理,系统开销很大,数据共享,程序结构都不方便,这时可使用多线程编程方法。
(2)并发程序设计
一个任务可能分成不同的步骤去完成,这些不同的步骤之间可能是松散耦合,可能通过线程的互斥,同步并发完成。这样可以为不同的任务步骤建立线程。
(3)网络程序设计
为提高网络的利用效率,我们可能使用多线程,对每个连接用一个线程去处理。
(4)数据共享
同一个进程中的不同线程共享进程的数据空间,方便不同线程间的数据共享。在多CPU系统中,实现真正的并行。
四、线程的基本操作
4.1、线程的创建
头文件:
#include <pthread.h>
函数:int pthread_create (pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
功能:创建一个新的子线程
参数:
thread:当前创建的线程id
attr:线程的属性,设置为NULL表示以默认的属性创建
start_routine:线程处理函数,如果当前函数执行完毕,则子线程也执行完毕
arg: 给线程处理函数传参用的
返回值:
成功:0
失败:非0
唯一难点的是 线程处理函数,也是此创建子线程运行的区域。(不管用不用都需要定义出来)
分析线程处理函数: void *(*start_routine) (void *)
返回类型void *,子线程名可以随意取,函数传参也是void *
即
void * pthread_fun1(void *arg)
{
printf("子线程工作区域\n");
}
4.2、创建一个子线程 (注意;当进程结束,线程也会结束,线程依赖于进程)
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
void * pthread_fun1(void *arg)
{
printf("子线程工作区域\n");
}
int main()
{
int ret;
pthread_t pthread;
int data=10;
printf("主控线程工作区域\n");
//ret=pthread_create(&pthread,NULL,pthread_fun1,NULL);//不传参,填NULL
ret=pthread_create(&pthread,NULL,pthread_fun1,(void *)&data);
if( ret != 0)
{
perror("fail to pthread_create");
exit(1);
}
while(1); //为什么需要用while,如果不用while,当主线程运行完,即进程运行结束,子线程就会释放
//也就是说,当main下面函数都执行完了,进程就结束了,不管子线程是否运行结束
}
注意:编译需要加 -lpthread 即:gcc 目标文件 -lpthread -o 执行文件(可随意取)
运行结果
主控线程工作区域
子线程工作区域
...
五、探究多个线程与主控线程执行顺序
5.1、当主控线程(即main函数)执行创建子线程时,是否接着执行后面语句。
答案:会接着执行
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
void * pthread_fun1(void *arg)
{
printf("子线程工作区域\n");
}
int main()
{
int ret;
pthread_t pthread;
int data=10;
printf("主线程工作区域\n");
ret=pthread_create(&pthread,NULL,pthread_fun1,(void *)&data);
if( ret != 0)
{
perror("fail to pthread_create");
exit(1);
}
printf("hello \n");
while(1);
return 0;
}
运行结果
主控线程工作区域
hello
子线程工作区域
...
问题又来了,一定是先运行hello,接着再运行 子线程??
答案:不一定,它们先后顺序,看它们谁先争夺CPU,谁先运行。
让hello 此语句睡眠一秒,代码如下所示
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
printf("子线程工作区域\n");
}
int main()
{
int ret;
pthread_t pthread;
int data=10;
printf("主线程工作区域\n");
ret=pthread_create(&pthread,NULL,pthread_fun1,(void *)&data);
if( ret != 0)
{
perror("fail to pthread_create");
exit(1);
}
sleep(1);
printf("hello \n");
while(1);
return 0;
}
运行结果
主控线程工作区域
子线程工作区域
hello
5.2、探究多个子线程运行顺序
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
printf("子线程1工作区域\n");
}
void * pthread_fun2(void *arg)
{
printf("子线程2工作区域\n");
}
int main()
{
int ret1;
int ret2;
pthread_t pthread1;
pthread_t pthread2;
int data=10;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
{
perror("fail to pthread_create2");
exit(1);
}
printf("hello1 \n");
printf("hello2 \n");
while(1);
return 0;
}
运行结果 顺序有多种(我只写一种)
主线程工作区域
子线程1工作区域
hello1
hello2
子线程2工作区域
总结,不管有多少个子线程,它们的顺序,由它们争夺CPU决定,谁先得到CPU 谁先运行。
六、探究线程共享数据
6.1,全局变量,子线程共享
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
int q =120;
void * pthread_fun1(void *arg)
{
printf("子线程1工作区域\n");
printf("q=%d\n",q);
// int a=*(int *)arg;
//printf("a= %d\n",a);
}
void * pthread_fun2(void *arg)
{
printf("子线程2工作区域\n");
printf("q=%d\n",q);
// int a=*(int *)arg;
// printf("a= %d\n",a);
}
int main()
{
int ret1;
int ret2;
pthread_t pthread1;
pthread_t pthread2;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
{
perror("fail to pthread_create2");
exit(1);
}
while(1);
return 0;
}
运行结果
主线程工作区域
子线程1工作区域
q=120
子线程2工作区域
q=120
6.2、子线程可以改变全局变量
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
int q =120;
void * pthread_fun1(void *arg)
{
printf("子线程1工作区域\n");
printf("q=%d\n",q);
q = q+10;
printf("子线程1 q = %d\n",q);
}
void * pthread_fun2(void *arg)
{
sleep(1);
printf("子线程2工作区域\n");
printf("子线程2 q = %d\n",q);
}
int main()
{
int ret1;
int ret2;
pthread_t pthread1;
pthread_t pthread2;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
{
perror("fail to pthread_create2");
exit(1);
}
sleep(2);
printf("主线程 %d\n",q);
while(1);
return 0;
}
运行结果
主线程工作区域
子线程1工作区域
q=120
子线程1 q = 130
子线程2工作区域
子线程2 q = 130
主线程 130
注意:主线程打印q时,需要提前创建好子线程,不然,会从main函数顺序执行。
七、线程传参
7.1、最基本线程传参
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
printf("子线程1工作区域\n");
int a=*(int *)arg;
printf("a= %d\n",a);
}
void * pthread_fun2(void *arg)
{
printf("子线程2工作区域\n");
int a=*(int *)arg;
printf("a= %d\n",a);
}
int main()
{
int ret1;
int ret2;
pthread_t pthread1;
pthread_t pthread2;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
{
perror("fail to pthread_create2");
exit(1);
}
while(1);
return 0;
}
运行结果
主线程工作区域
子线程2工作区域
a= 100
子线程1工作区域
a= 100
7.2、子线程改变传参的值
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
printf("子线程1工作区域\n");
int a=*(int *)arg;
printf("a= %d\n",a);
a++;
printf("a= %d\n",a);
*(int *)arg=1200;
}
void * pthread_fun2(void *arg)
{
printf("子线程2工作区域\n");
sleep(1);
int b=*(int *)arg;
printf("b= %d\n",b);
}
int main()
{
int ret1;
int ret2;
pthread_t pthread1;
pthread_t pthread2;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if((ret2=pthread_create(&pthread2,NULL,pthread_fun2,(void *)&data))!=0)
{
perror("fail to pthread_create2");
exit(1);
}
while(1);
return 0;
}
运行结果
主线程工作区域
子线程1工作区域
a= 100
a= 101
子线程2工作区域
b= 1200
八、pthread——join等待子线程
头文件:
#include <pthread.h>
函数:int pthread_join(pthread_t thread, void **retval);
功能:1)阻塞等待一个子线程的退出,
2)可以接收到某一个子线程调用pthread_exit时设置的退出状态值
参数:
thread:指定线程的id
retval:保存子线程的退出状态值,如果不接受则设置为NULL
返回值:
成功:0
失败:非0
扩展;传参
定义 int a -——main——f(a) -——子函数——f(int b) —取出来—b
定义 int *a -——main——f(&a) -——子函数——f(int **b) —取出来—*b
九、pthread_create 搭配函数 pthread_join等待子线程退出
9.1、pthread_join不接收返回值(代码)
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
static int re_val =100;
printf("子线程1工作区域\n");
sleep(3);
printf("end \n");
return (void *)&re_val;
}
int main()
{
int ret1;
pthread_t pthread1;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if(pthread_join(pthread1,NULL)!=0)
{
perror("fail to pthread_join");
exit(1);
}
return 0;
}
运行结果
主线程工作区域
子线程1工作区域
end
9.2、pthread——join 接收返回值(代码)
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
static int re_val =100;
printf("子线程1工作区域\n");
sleep(3);
printf("end \n");
return (void *)&re_val;
}
int main()
{
int ret1;
pthread_t pthread1;
int data=100;
int *renum;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if(pthread_join(pthread1,(void**)&renum)!=0)
{
perror("fail to pthread_join");
exit(1);
}
printf("renum = %d\n",*(int *)renum);
return 0;
}
运行结果
主线程工作区域
子线程1工作区域
end
renum = 100
问题1: 当main函数存在一条语句在pthread_join后面,我们发现不管怎么运行,都是先运行完子线程,才运行此语句。但这不是我想要结果,我想要的是各自运行各自的,互不干扰。
因此,我们需要使用另外一种函数——pthread_detach分离函数,同时也自动回收子线程数据。
但是此函数不会等待子线程结束。
十、pthread_detach
头文件
#include <pthread.h>
函数:int pthread_detach(pthread_t thread);
功能:使调用线程与当前进程分离,使其成为一个独立的线程。
该线程终止时,系统将自动回收它的资源。
参数:
thread:指定的子线程的id
返回值:
成功:0
失败:非0
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
static int re_val =100;
printf("子线程1工作区域\n");
sleep(1);
printf("子线程1开始工作\n");
sleep(1);
printf("子线程1工作结束\n");
}
int main()
{
int ret1;
pthread_t pthread1;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
#if 0
if(pthread_join(pthread1,NULL)!=0)
{
perror("fail to pthread_join");
exit(1);
}
#endif
if(pthread_detach(pthread1) !=0 )
{
perror("fail to pthread_detach");
exit(1);
}
while(1)
{
printf("Hello world\n");
sleep(1);
}
return 0;
}
运行结果
主线程工作区域
Hello world
子线程1工作区域
Hello world
子线程1开始工作
Hello world
子线程1工作结束
Hello world
不等待子线程结束演示过程
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
static int re_val =100;
printf("子线程1工作区域\n");
sleep(1);
printf("子线程1开始工作\n");
sleep(1);
printf("子线程1工作结束\n");
}
int main()
{
int ret1;
pthread_t pthread1;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if(pthread_detach(pthread1) !=0 )
{
perror("fail to pthread_detach");
exit(1);
}
printf("hello\n");
return 0;
}
运行结果
主线程工作区域
hello
结果如上,并没有等待且去执行完子线程,因此,我们常用还是phread_join。只需要把phread_join放在main函数最一个语句上,运行方式与phread_detach,类同。
十一、pthread_self 查询pthrea_ID
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
static int re_val =100;
printf("子线程1工作区域\n");
printf("pthread_ID = %ld\n",(unsigned long )pthread_self());
sleep(3);
printf("end \n");
return (void *)&re_val;
}
int main()
{
int ret1;
pthread_t pthread1;
int data=100;
int *renum;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
if(pthread_join(pthread1,(void**)&renum)!=0)
{
perror("fail to pthread_join");
exit(1);
}
printf("renum = %d\n",*(int *)renum);
return 0;
}
运行结果
主线程工作区域
子线程1工作区域
pthread_ID = 140598107539200
end
renum = 100
问题:前面我们都是学习子线程运行完才自动结束,可我不想,我想运行到一半就结束,或者到某个设定的语句就结束,因此,我们需要学习一个线程退出函数——pthread_exit
十二、线程退出函数
头文件
#include <pthread.h>
函数:void pthread_exit(void *retval);
功能:退出正在执行的线程
参数:
retval:当前线程的退出状态值,这个值可以被调用pthread_join函数的线程接收到
返回值:无
12.1无返回值
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
while(1)
{
static int cnt =0;
cnt++;
printf("***************\n");
sleep(1);
if(cnt==5)
{
pthread_exit(NULL);
}
}
}
int main()
{
int ret1;
pthread_t pthread1;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
printf("hello world\n");
if(pthread_join(pthread1,NULL)!=0)
{
perror("fail to pthread_join");
exit(1);
}
return 0;
}
运行结果
主线程工作区域
hello world
***************
***************
***************
***************
***************
12.2有返回值(以返回字符串为例)
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include <unistd.h>
void * pthread_fun1(void *arg)
{
static char buf[]="this is pthread";
while(1)
{
static int cnt =0;
cnt++;
printf("***************\n");
sleep(1);
if(cnt==5)
{
pthread_exit(buf);
}
}
}
int main()
{
int ret1;
pthread_t pthread1;
int data=100;
printf("主线程工作区域\n");
if((ret1=pthread_create(&pthread1,NULL,pthread_fun1,(void *)&data))!=0)
{
perror("fail to pthread_create1");
exit(1);
}
printf("hello world\n");
char *str;
if(pthread_join(pthread1,(void**)&str)!=0)
{
perror("fail to pthread_join");
exit(1);
}
printf("str = %s\n",str);
return 0;
}
运行结果
主线程工作区域
hello world
***************
***************
***************
***************
***************
str = this is pthread
你学会了???