并行和并发的区别:
1.并发(concurrency):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。
并发是指同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上有多个进程被同时执行的效果–宏观上并行,针对单核处理器
其中两种并发关系分别是同步和互斥。
- 互斥:进程间相互排斥的使用临界资源的现象,就叫互斥。
- 同步(synchronous):进程之间的关系不是相互排斥临界资源的关系,而是相互依赖的关系。
进一步的说明:就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。(彼此有依赖关系的调用不应该同时发生,而同步就是阻止那些“同时发生”的事情
其中并发又有伪并发和真并发,伪并发是指单核处理器的并发,真并发是指多核处理器的并发。
2.并行(parallelism):在单处理器中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特种;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多处理器上的程序才可实现并行处理。
从而可知,并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也亦是说并发事件之间不一定要同一时刻发生。(同一时刻,有多条指令在多个处理器上同时执行–针对多核处理器)
同步和异步的区别:
- 同步(synchronous):进程之间的关系不是相互排斥临界资源的关系,而是相互依赖的关系。进一步的说明:就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。
- 异步(asynchronous):异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。
线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。
1、线程的概念:
线程是进程的一条执行流,是cpu调度的基本单位,一个进程可以有多个线程;线程是进程中的一条执行流,是CPU调度的基本单位,一个进程中可以有多个线程;
Linux下的线程执行流是通过pcb实现的,且一个进程中可能有多个pcb,并且这些pcb共享同一个进程中的大部分资源,因此,也被称为轻量级进程;
- LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下)
- 进程:独立地址空间,拥有PCB
- 线程:也有PCB,但没有独立的地址空间(共享)
- 区别:在于是否共享地址空间。 独居(进程);合租(线程)。
Linux下:
线程:最小的执行单位,调度的基本单位。
进程:资源分配的基本单位,可看成是只有一个线程的进程。
1.2.线程之间的独有与共享
- 共享:虚拟地址的空间(代码段、数据段、堆…)、文件描述符、信号处理方式、工作路径…
- 独有:线程栈、寄存器、信号屏蔽字、优先级、errno、线程标识符ID…
1.3 多进程与多线程多任务处理优缺点分析
共同优点:
- 在CPU资源足够的情况下都可以实现并行的任务处理,提高效率;
- IO密集型程序(程序中进行大量的IO操作,对CPU资源要求不高)中,同时发起IO操作减少IO阻塞时间;
- CPU密集型程序(程序中进行大量的数据运算,对CPU资源要求高)中,执行流的数量并不是越多越好,多了反而会增加调度成本;
多线程进行多任务
线程优点:
(1)共享虚拟地址空间,因此线程间通信更加灵活(还可以通过全局变量、函数传参来实现通信);
(2)线程的创建和销毁成本更低;
(3)同一个进程中的线程调度切换成本更低;
线程缺点:
健壮性低,有些调用和异常是针对整个进程产生的;
例如:exit接口,进程退出会使进程中的所有线程都退出;
大多数情况下,使用多线程,在稳定性要求高的场景中使用多进程;
进程优点:
稳定、健壮性高。(异常导致进程退出,所有线程退出,但不影响其他进程)–主进程安全性要求高的场景–比如:shell、服务器。
2.线程控制(创建、退出、等待、分离)
linux并没有向上提供用于创建线程的接口。大佬们对系统调用接口进行封装实现了上层用户态的线程控制接口;
- 我们操作线程的接口都是库函数,需要引入头文件<pthread.h>;
- 链接这些线程函数库时要使用编译器命令的"-lpthread"选项
2.1.创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
返回值:成功返回0;失败返回非0;
-
thread:用于获取线程ID;
-
attr:设置线程属性,设置为NULL,表示默认属性;
-
start_routine:线程的入口函数;
-
arg:通过线程入口函数给线程传递的参数;
2.2 线程终止
- 线程入口函数中执行return(线程入口函数运行完毕,线程退出,);
- 调用pthread_exit终止自己;
- 调用pthread_cancel终止同一进程中的另一个线程;
void pthread_exit(void *retval);
//retcal:返回值
//可以在任意位置,哪个线程调用哪个线程退出;
int pthread_cancel(pthread_t thread);//被动退出
//thread:一个指定的线程ID
//传入谁的ID谁退出;
//返回值:成功返回0,失败返回非0;
注意:主线程退出并不会导致进程退出,只有所有线程退出了进程才会退出;
代码实现
//代码实现:linux下
#include <stdio.h>
#include <pthread.h>
void *thr_entry(void *arg)
{
printf("%s\n", (char*)arg);
while(1) {
printf(" i am normal thread-%p-%d\n", pthread_self(), getpid());
sleep(1);
pthread_exit(NULL);
}
return NULL;
}
int main ()
{
pthread_t tid;
int ret;
char *ptr = "今天的天气好晴朗\n";
pthread_create(获取ID, 属性, 入口函数, 传入参数)
ret = pthread_create(&tid, NULL, thr_entry, (void*)ptr);
if (ret != 0) {
printf("pthread_create failed!\n");
return -1;
}
sleep(2);
pthread_cancel(tid);
while(1) {
printf("i am main thread--%p-%d\n", pthread_self(), getpid());
sleep(1);
}
return 0;
}
2.3.线程等待
线程默认情况下退出后,资源不会被回收,需要被其他线程等待,获取退出线程的返回值释放资源。
线程也不是必须被等待,在线程属性中,有一个分离属性,默认是joinable,处于joinable状态的线程退出后不会自动释放资源,需要被等待;
int pthread_join(pthread_t thread, void **retval);
//thread:线程ID
//**retval: 返回值
//pthread_join是一个阻塞接口
//返回值:成功返回0,失败返回错误码
代码实现
//代码实现,linux
#include<stdio.h>
2 #include<pthread.h>
3 void* thr_entry(void* arg)
4 {
5 char *ptr="hello pthread\n";
6//
7 sleep(3);
8 return (void*)ptr;
9
10 }
11 int main(int argc,int *argv[])
12 {
13 pthread_t tid;
14 int ret=pthread_create(&tid,NULL,thr_entry,NULL);
15 if(ret!=0)
16 {
17 printf("thread create error\n");
18 return -1;
19 }
20
21 //pthread_join(线程id,获取返回值)
22 void *retval=NULL;
23 pthread_join(tid,&retval);
24 printf("retval:%s\n",retval);
25 while(1)
26 {
27 printf("main thread\n");
28 sleep(1);
29 }
30 return 0;
31
32 }
2.4 线程分离
如果你对一个线程的返回值不关心,你也不想去等待这个线程的推出;
设置线程分离属性为detach属性,处于detach属性的线程退出后自动释放资源,不需要被等待;
int pthread_detach(pthread_t thread);
//pthrad:分离线程的id
//可以在任意位置;
pthread_detach(pthread_self());
//pthread_self()函数,获取调用函数id;见下图:
// 线程自己分离
如果不知道,man函数里的左上角()中的数字代表什么,请看上一篇博客;
https://blog.csdn.net/weixin_52270223/article/details/115710222?spm=1001.2014.3001.5501
线程安全等问题清见下一篇博客;**
如有错误或者补充,评论下;互相学习,互关一波,抱拳了