您的位置:首页 > 职场人生

多线程这些都不清楚,你面试?还不赶紧看看?细到恐怖....

2021-04-19 08:46 861 查看

并行和并发的区别:

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 &lt;stdio.h&gt;
#include &lt;pthread.h&gt;

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状态的线程退出后不会自动释放资源,需要被等待;**

![](https://img-blog.csdnimg.cn/20210418120711230.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81MjI3MDIyMw==,size_16,color_FFFFFF,t_70)
```cpp
int pthread_join(pthread_t thread, void **retval);
//thread:线程ID
//**retval: 返回值
//pthread_join是一个阻塞接口
//返回值:成功返回0,失败返回错误码

代码实现

//代码实现,linux
#include&lt;stdio.h&gt;
2 #include&lt;pthread.h&gt;
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属性的线程退出后自动释放资源,不需要被等待;

```c
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

同文博客:https://blog.csdn.net/weixin_52270223/article/details/115820547

线程安全等问题清见下一篇博客;
如有错误或者补充,评论下;互相学习,互关一波,抱拳了**

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: