一、线程的基本概念
1、线程是在进程内部运行的一个执行分支
2、线程的创建成本比较小,删除进程成本也比较小,只需要创建一个PCB,指向那块内存地址空间就行。
3、线程是在进程内部运行,本质上是在同一块地址空间上运行。
怎么理解一个进程中间会有多个执行流的?
一个执行流被一个线程表示,一个进程里面可以有多个线程,所以一个进程里面会有多个执行流。
4、站在cpu的角度不需要区分进程与线程,因为cpu所做的工作很简单,就是去执行传递过来的指令
Linux cpu识别到的pcb或所看到的进程都可以识别为轻量级进程
进程是承担系统分配资源的一个基本实体。
线程是cpu调度的一个基本单位。
设置进程就是为了强调资源独占,设置线程就是为了体现资源共享,
5、每个线程都有自己的私有栈结构,每个线程都有自己的上下文信息
进程和线程的区别:
1、进程是资源竞争的基本单位
2、线程是程序执⾏行的最⼩小单位
- 3、线程共享进程数据,但也拥有⾃自⼰己的⼀一部分数据: >* 线程ID >* ⼀一组寄存器 >* 栈 >* errno >* 信号 屏蔽字 >* 调度优先级
由于同一进程的多个线程共享同一块地址空间,因此Text Segment、Data Segegment、Data Segement都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
- 1、文件描述符表
- 2、每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)
- 3、当前工作目录
- 4、用户id和组id
以下资源是每个线程各有一份:
- 1、线程id
- 2、上下文,包括各种寄存器的值、程序计数器和栈指针
- 3、栈空间
- 4、errno变量
- 5、信号屏蔽字
- 6、调度优先级
6、线程的优点:
- 1、创建⼀一个新线程的代价要⽐比创建⼀一个新进程⼩小得多
- 2、 与进程之间的切换相⽐比,线程之间的切换需要操作系统做的工作要少很多
- 3、线程占⽤用的资源要⽐比进程少很多
4、能充分利⽤用多处理器的可并⾏行数量
5、在等待慢速I/O操作结束的同时,程序可执⾏行其他的计算任务
- 6、计算密集型应⽤用,为了能在多处理器系统上运⾏行,将计算分解到多个线程中实现
- 7、 I/O密集型应⽤用,为了提⾼高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
-线程的缺点:
1、性能损失
2、健壮性降低
3、缺乏访问控制
4、编程难度提高
二、线程的创建:
创建函数:
1、
功能:创建⼀一个新的线程 原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*star t_routine)(void*), void *arg);
参数
- thread:返回线程ID
- attr:设置线程的属性,attr为NULL表⽰示使⽤用默认属性
start_routine:是个函数地址,线程启动后要执⾏行的函数
arg:传给线程启动函数的参数 返回值:成功返回0;失败返回错误码
代码:
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 void *thread_run(void * arg)
5 {
6 while(1){ printf("new thread,thread is :%lu,pid:%d\n",pthread_self(),getpid());
7 sleep(1);
8 // return (void *) 1;
9 }
10 }
11 int main()
12 {
13 pthread_t tid;
14 pthread_create(&tid,NULL,thread_run,NULL);//1、取地址新的tid2、线程属性
15 // 3、线程执行函数4、要传递给它的参数
16 while(1)
17 {
18 printf("main thread,thread is :%lu,pid:%d\n",pthread_self(),getpid());
19 sleep(1);
20 }
21 return 0;
22 }
程序运行结果:
查看进程和轻量级进程
解释:
三、线程等待函数:
功能:等待线程结束 原型
int pthread_join(pthread_t thread, void **value_ptr);
参数
thread:线程ID
value_ptr:它指向⼀一个指针,后者指向线程的返回值 返回值:成功返回0;失败返回错误码
1 #include<stdio.h>
2 #include<pthread.h>
3 #include<unistd.h>
4 void *thread_run(void * arg)
5 {
6 sleep(4);
7 printf("new thread,thread is :%lu,pid:%d\n",pthread_self(),getpid());
8 // sleep(1);
9 return (void *) 123;
10
11 }
12 int main()
13 {
14 pthread_t tid;
15 pthread_create(&tid,NULL,thread_run,NULL);//1、取地址新的tid2、线程属性
16 // 3、线程执行函数4、要传递给它的参数
17 // while(1)
18 // {
19 // printf("main thread,thread is :%lu,pid:%d\n",pthread_self(),getpid());
20 // sleep(1);
21 //}
22 void *ret;
23 pthread_join(tid,&ret);
24 printf("join new thread success,ret:%d\n",(int)ret);
25 return 0;
26 }
四、 线程终止函数
在线程的内部要慎重的调用exit,一旦调用exit,是进程终止而不是线程终止,要线程中止就要调用pthread_exit();
1、pthread_exit((void*)123);
这种方式是线程终止
2、exit(123)
这种方式是进程终止
五、线程取消函数
1、线程取消成功返回值为-1
pthread_cancel(tid);
2、线程还可以自己取消
pthread_cancel(pthread_self());
代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<pthread.h>
4 #include<unistd.h>
5 void *thread_run(void * arg)
6 {
7 printf("new thread,thread is :%lu,pid:%d\n",pthread_self(),getpid());
8 sleep(4);
9 pthread_cancel(pthread_self());
10 sleep(10);
11 // return (void *) 123;
12 pthread_exit((void*)123);
13
14 }
15 int main()
16 {
17 pthread_t tid;
18 pthread_create(&tid,NULL,thread_run,NULL);//1、取地址新的tid2、线程属性
19 // 3、线程执行函数4、要传递给它的参数
20 sleep(1);
21 pthread_cancel(tid);
22 // while(1)
23 // {
24 // printf("main thread,thread is :%lu,pid:%d\n",pthread_self(),getpid());
25 // sleep(1);
26 //}
27 void *ret;
28 pthread_join(tid,&ret);
29 printf("join new thread success,ret:%d\n",(int)ret);
30 return 0;
31 }
- 3、从线程函数return。这种方法对主线程不适用,从main函数return相当于调用exit。
六、总结:
- 1、如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。
- 2、如果thread线程被别的线程调用用pthread_cancle异常终掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED。
- 3、如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元里存放的是传给pthread_exit的参数。如果对thread线程的终止状态不敢兴趣,可以传NULL给value_ptr参数。