多线程-概述及底层实现机制浅析

2018-09-12 18:26:28 浏览数 (1)

前言

在打算写这篇多线层底层实现机制的时候,突然发现自己对于计算机竟然懂得这么表面,对于CPU的工作原理都不完全清楚,于是又转头查看了一些CPU相关的资料。也不敢钻的太深,怕自己迷路...,其中如有错误,望知道的朋友在下面留言评论,我会及时更新的。


关于CPU

CPU(中央处理器)顾名思义是电脑中处理器的总部(电脑里几个重要的硬件都自带的有自己的处理器,比如,GPU是显卡自己的处理器等),负责整个电脑的运行调度。它是指挥者,所以最底层的一些工作并不是由CPU来完成的。CPU以及高的速度再运转,它的运行是由一串串数字脉冲信号通过硅晶版上的晶体管实现的,那一波波的数字脉冲信号震荡的速度,就是CPU那传说中的主频

CPU的主频与CPU实际的运算能力并没有直接关系。如今的CPU工艺先进,运算流水线短,其主频的高低在一定意义上又可以体现CPU性能的高低。CPU的主频不代表CPU的速度,但提高主频对于提高CPU运算速度却是至关重要的。

关于CPU使用率,其实就是你运行的程序占用的CPU资源,表示你的机器在某个时间点的运行程序的情况。使用率越高,说明你的机器在这个时间上运行了很多程序,反之较少。使用率的高低与你的CPU强弱有直接关系。现代分时多任务操作系统对 CPU 都是分时间片使用的:比如A进程占用10ms,然后B进程占用30ms,然后空闲60ms,再又是A进程占10ms,B进程占30ms,空闲60ms;如果在一段时间内都是如此,那么这段时间内的占用率为40%。CPU对线程的响应并不是连续的,通常会在一段时间后自动中断线程。未响应的线程增加,就会不断加大CPU的占用。

操作系统的设计,可以归结为三点:

  • (1)以多进程形式,允许多个任务同时运行;
  • (2)以多线程形式,允许单个任务分成不同的部分运行;
  • (3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。

CPU型号

CPU内核信息

I3

双核四线程

I5

双核四线程

I5

四核四线程

I7

四核八线程

I7

六核十二线程

关于CPU的详细介绍可以看这里

关于多核、多处理器、多线程

了解了CPU的工作机制,我们再来分析一下CPU的多核、多处理器,与多线程。也许有人会问:“了解这些有什么卵用”,其实在我看来,详细了解这些硬件对于我们理解多线程技术的必要性和开展多线程编程都是很必要的,可以提供宏观的理论依据。

多核

CPU的性能主要靠提高核心工作频率来提高,由于物理限制,不能把CPU的核心频率无限提高,所以发展出来双核心或多核心的CPU。相当于在一枚处理器上集成多个完整的计算引擎(内核),他们共享缓存,内存,寄存器等。

,是物理的,几核就是真的有几个物理核心。线程就是英特尔的超线程技术。是在一个实体处理器中,提供两个逻辑线程。线程是虚拟的,不是真实存在的。但性能上,会模拟真实的核。也就是说,双核4线程,看起来很像4核,但比真实的4核4线程慢,却比双核双线程快。

手机多核其实应该叫多CPU,将多个CPU芯片封装起来处理不同的事情,你甚至可以戏称为“胶水核心”,也就是被强行粘在一起的意思。在待机或者空闲的时候,八核的手机也只能用到一到两个核心。

而电脑则不同,PC的多核处理器是指在一个处理器上集成了多个运算核心,通过相互配合、相互协作可以处理同一件事情,是多个并行的个体封装在了一起。用一句话概括,就是并行处理,双核就是单车道变多车道 我们都知道智能手机都是有CPU的,iPhone6使用的A8处理器是一款双核处理器。

多处理器(多CPU)

多处理器系统是指包含两台或多台功能相近的处理器(多CPU),处理器之间彼此可以交换数据,所有处理器共享内存,I/O设备,控制器,及外部设备,整个硬件系统由统一的操作系统控制,在处理器和程序之间实现作业、任务、程序、数组极其元素各级的全面并行。目前主流的服务器架构,超级计算机等等,都是多CPU多核架构。

多线程

多线程是为了使得多个线程并行的工作以完成多项任务,以提高系统的效率。线程是在同一时间需要完成多项任务的时候被实现 的。

多线程实现的相关讨论

下面进入到正餐了,在讨论多线程之前,我们需要先认识一下,进程、线程,以及相关值得注意的问题。

什么是线程、进程

来来来,先读一个有趣的故事《进程与线程的一个简单解释》,浅显易懂,生动形象地解释了多项成相关的很多典型问题。通过上文我们大致可以宏观了解这些问题了。

进程: 进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是计算机中已运行程序的实体。其本身并不是几部运行单位,是线程的容器。 线程: 线程是操作系统能够进行运算调度的最小单位。线程是一组指令的集合它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程是独立调度和分派的基本单位。同一进程中的多条线程将共享该进程中的全部系统资源,但是自有调用堆栈和寄存器环境。

线程、进程的不同

主线程在程序中的地位和其他线程不同,它是其他线程最终的父线程,且所有界面的显示操作即AppKit或 UIKit的操作必须在主线程进行。

进程和线程都是操作系统的概念。进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成。

线程和进程十分相似,不同的只是线程比进程小。首先,线程采用了多个线程可共享资源的设计思想;例如,它们的操作大部分都是在同一地址空间进行的。其次,从一个线程切换到另一线程所花费的代价比进程低。再次,进程本身的信息在内存中占用的空间比线程大,因此线程更能允分地利用内存。

线程是进程的一部分 CPU调度的是线程 系统为进程分配资源,不对线程分配资源

进程和线程的关系:

一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。 资源分配给进程,同一进程的所有线程共享该进程的所有资源。 处理机分给线程,即真正在处理机上运行的是线程。 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

为什么要实现多线程

多线程技术的出现,主要是因为多任务的需要,比如我想同时写文章和听歌,如果我们的CPU一直被写文章的任务占着,等写文章的任务结束后,再播放歌曲,很显然达不到需要的“同时”做两件事的效果。

多线程的出现也是多核CPU、多处理器计算机普及的需要,这样可以提高CPU的利用率,增加并发性,提高程序的执行效率,更快的处理完任务。

在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较游泳了。在这种情况下我们可以释放一些珍贵的资源如内存占用等等。

多线程为何能实现

单就一个CPU而言两个线程可以解决线程阻塞造成的不流畅问题,其本身运行效率并没有提高,多CPU的并行运算才真正解决了运行效率问题,这也正是并发和并行的区别。当然,不管是多核还是单核开发人员不用过多的担心,因为任务具体分配给几个CPU运算是由系统调度的,开发人员不用过多关心系统有几个CPU。开发人员需要关心的是线程之间的依赖关系,因为有些操作必须在某个操作完成完才能执行,如果不能保证这个顺序势必会造成程序问题。 每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。调度程序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。

每个进程都有私有的虚拟地址空间,进程的所有线程共享同一地址空间。每个线程被CPU分配一个时间片,一旦被激活,它正常运行直到时间片耗尽并被挂起,此时,操作系统选择另一个线程进行运行。通过时间片轮转,又出于各个时间片很小(20毫秒级),看起来就像多个线程同时在工作。实际上,只有在多处理器系统上才是真正的在可得到的处理器上同时运行多个线程。

从一个进程切换到另一个进程是需要一定时间的--保存和装入寄存器值及内存映像,更新各种表格和队列等。假如进程切换(process switch) - 有时称为上下文切换(context switch),需要5毫秒,再假设时间片设为20毫秒,则在做完20毫秒有用的工作之后,CPU将花费5毫秒来进行进程切换。CPU时间的20%被浪费在了管理开销上。

时间片设得太短会导致过多的进程切换,降低了CPU效率;而设得太长又可能引起对短的交互请求的响应变差。将时间片设为100毫秒通常是一个比较合理的折衷。

线程越多越好吗

线程多了可以提高程序并行执行的速度,但是并不是越多越好。 其一,每个线程都要占用内存,多线程就意味着更多的内存资源被占用,开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。 其二,从微观上讲,一个cpu不是同时执行两个线程的,他是轮流执行的,所以线程太多,cpu必须不断的在各个线程间快回更换执行,线程间的切换无意间消耗了许多时间,所以cpu有效利用率反而是下降的

开发中实现多线程的方案

Mach是第一个以多线程方式处理任务的系统,因此多线程的底层实现机制是基于Mach的线程。 然后开发中很少用Mach级的线程,因为Mach级的线程没有提供多线程的基本特征,线程之间是独立的

多线程的方案有以下几种

  • C语言的POSIX接口方案 Pthreads定义了一套C语言的类型、函数与常量,它以pthread.h头文件和一个线程库实现。线程库实行了POSIX线程标准通常称为Pthreads。POSIX线程具有很好的可移植性,使用pthreads编写的代码可运行于Solaris、FreeBSD、Linux 等平台,Windows平台亦有pthreads-win32可供使用。而且你需要手动处理线程的各个状态的转换即管理生命周期,比如,这段代码虽然创建了一个线程,还需要销毁这个线程。感兴趣的朋友,或者说想要自己实现一套多线程方案,从底层开始定制,那么可以去搜一下相关资料研究一下。
  • OC的NSThread方案。是对POSIX thread的封装
  • C语言的GCD接口方案(性能最好,代码更精简)。
  • OC的NSOperation和NSOperationQueue(基于GCD)方案。

小结

本文洋洋洒洒写了好多看似跟多线程无关的东西,其实我认为这些对之后多线程的学习是有帮助的,如果你认真对了本文,相信你会对多线程有更深的理解,作为多线程系列的开胃菜,本文到此就结束了,后续会陆续发布多线程相关的文章,如有不对之处,还望观看的朋友,在下面的评论区指出,thanks.

0 人点赞