现代电脑已经具有同一时间执行多个操作的能力。由于硬件的发展以及更智能的操作系统的支持,我们的程序可以执行反馈的更快。
由于这个巨大的能力(同一时间执行多个操作),编程变得十分神奇同时又十分复杂。这项巨大的能力使得你必须去理解电脑内部的执行原理。在第一节中,我将解开多线程的一角,讲解多线程这项如此有魔力的东西。

进程与线程:以正确的方式命名事物

现代的操作系统可以在同一时间执行多个程序。那就是为什么你可以一边用你的浏览器看文章一边又可以听音乐的原因。每个程序都被看做一个进程被执行。操作系统知道许多软件技巧来使多个程序一起运行,当然也可以使用底层硬件。不管何种方式,最终的结果就是让你觉得你所有的程序都在同一时间进行着。

在操作系统中运行进程并不是同时执行多个操作的唯一方法。每个进程也可以同时运行他的子任务——线程。在被启动的时候,每个进程至少会出发一个被叫做主线程的线程。然后根据程序的需要,额外的线程或被启动或被终止。多线程是指在一个进程中运行多个线程。

例如,您的媒体播放器可能运行多个线程:一个用于呈现界面-这通常是主线程,另一个用于播放音乐等。

你可以这样去想:操作系统是一个包含许多进程的容器。其中每个进行又是多个线程的容器。在这个文章里面,我将只关注于线程但是以上的所有主题都是有趣值得深究的。

Operating systems can be seen as a box that contains processes, which in turn contain one or more threads.

操作系统可以被看做一个包含进程的大盒子,而每个进程又包含了一个或多个线程(至少一个主线程)

进程与线程之间的区别

每个进程都拥有一个属于自己的内存块。这些自己的内存块无法被其他的进程访问分享:你的浏览器程序无法访问媒体播放器的内存,反之亦然。当然,如果你同时启动的相同程序两次(例如打开了谷歌浏览器两次),他们之间也无法进行内存之间的访问。操作系统将每个实例视为一个新进程,并分配其各自独立的内存部分。因此,默认情况下,两个或多个进程无法共享数据,除非它们执行高级技巧——所谓的进程间通信(IPC)

与进程不同,线程共享操作系统分配给其父进程的同一块内存:媒体播放器主界面中的数据可以被音频引擎轻松访问,反之亦然。因此,两个线程更容易相互通信。除此之外,线程通常比进程更轻:它们占用的资源更少,创建速度更快,这就是为什么它们也被称为轻量级进程的原因。

线程是使程序同时执行多个操作的简便方法。如果没有线程,您将不得不为每个任务编写一个程序,将它们作为进程运行,并通过操作系统对它们进行同步。这将更加困难(IPC比较棘手),并且速度较慢(进程比线程更重)。

Green threads, of fibers

到目前为止提到的线程是一个操作系统的事情:一个想要启动一个新线程的进程必须与操作系统对话。不过,并非每个平台都支持线程。绿色线程(也称为光纤)是一种仿真,使多线程程序在不提供该功能的环境中工作。例如,如果底层操作系统没有本机线程支持,虚拟机可能实现绿色线程。

绿色线程的创建和管理速度更快,因为它们完全绕过了操作系统,但也有缺点。我将在下一集中写下这个话题。

“绿色线程”的名字是指Sun MyStand在90年代设计了原始Java线程库的绿色团队。如今Java不再使用绿色线程:它们在2000返回到本地的线程。其他一些编程语言(go、haskell或rust等)实现了绿色线程的等价物,而不是本机线程。

线程被用于什么

为什么一个进程应该使用多个线程?正如我之前提到的,并行工作大大加快了速度。假设您将要在电影编辑器中渲染电影。编辑器可能足够智能,可以将渲染操作分散到多个线程上,每个线程在多个线程上处理最终电影的一块。因此,如果使用一个线程,那么任务需要一个小时,而使用两个线程,则需要30分钟;使用四个线程,则需要15分钟,依此类推。

真的那么简单吗?有三点需要考虑:

不是每个程序都需要多线程。如果你的应用程序执行顺序操作或者经常等待用户做一些事情,多线程可能没有那么好;

您只需不向应用程序抛出更多的线程,以使其更快地运行:每个子任务都必须经过仔细的思考和设计,才能执行并行操作;

不能100%地保证线程将真正并行地执行其操作,也就是说,它实际上取决于底层硬件。

最后一个是至关重要的:如果您的计算机不同时支持多个操作,那么操作系统必须伪造它们。我们马上就知道。现在,让我们把并发性看作是同时运行任务的感知,而真正的并行性则是同时运行的任务。

(个人:单个CPU在执行指令的时候是一条一条执行的:通过CS:IP来获取指令)

什么使并发性和并行性成为可能?

计算机中的中央处理器(CPU)完成运行程序的繁重工作。它由几个部分组成,主要部分是所谓的核心部分:这就是实际执行计算的地方。核心一次只能运行一个操作。

这当然是一个主要的缺点。因此,操作系统开发了先进的技术,使用户能够同时运行多个进程(或线程),特别是在图形环境中,甚至在单核机器上。最重要的一个任务称为抢占式多任务处理,抢占是中断一个任务,切换到另一个任务,然后在以后恢复第一个任务的能力。

因此,如果您的CPU只有一个内核,那么操作系统的一部分工作就是将单核计算能力分散到多个进程或线程上,这些进程或线程在循环中一个接一个地执行。这个操作会让你产生一个以上的程序并行运行的错觉,或者一个程序同时做多个事情(如果是多线程的话)。虽然满足并发性,但真正的并行性——同时运行进程的能力——仍然缺失。

今天,现代CPU在引擎盖下有多个核心,每个核心一次执行一个独立的操作。这意味着对于两个或多个内核,真正的并行性是可能的。例如,我的Intel Core i7有四个内核:它可以同时运行四个不同的进程或线程。

操作系统能够检测CPU核心的数量,并为每个核心分配进程或线程。线程可以分配给操作系统喜欢的任何核心,并且这种调度对于正在运行的程序是完全透明的。另外,如果所有核心都忙的话,先发制人的多任务可能会出现。这使您能够运行比计算机中可用的实际数量或核心更多的进程和线程。

单核多线程应用程序:有意义吗?

在单核机器上实现真正的并行是不可能的。然而,如果您的应用程序可以从中受益,那么编写多线程程序仍然是有意义的。当一个进程使用多个线程时,即使其中一个线程执行缓慢或阻塞的任务,抢占式多任务也可以保持应用程序的运行。

比如说,你正在开发一个桌面应用程序,它从一个非常慢的磁盘上读取一些数据。如果只使用一个线程编写程序,整个应用程序将冻结,直到磁盘操作完成:分配给唯一线程的CPU电源在等待磁盘唤醒时被浪费。当然,操作系统正在运行除此之外的许多其他进程,但是您的特定应用程序将不会有任何进展。

让我们以多线程的方式重新考虑您的应用程序。线程A负责磁盘访问,而线程B负责主接口。如果线程A由于设备速度慢而被卡住,那么线程B仍然可以运行主界面,从而保持程序响应。这是可能的,因为有两个线程,操作系统可以在两个线程之间切换CPU资源,而不会陷入较慢的线程。

(个人:I/O等待的时候就可以执行其他的任务,而不是呆呆的在等待)

更多线程,更多问题

如我们所知,线程共享其父进程的同一块内存。这使得他们中的两个或多个在同一个应用程序中交换数据非常容易。例如:一个电影编辑器可能拥有一大部分包含视频时间线的共享内存。这样的共享内存正被几个指定用于将电影呈现到文件中的工作线程读取。它们都只需要一个指向该内存区域的句柄(例如指针),就可以从该内存区域中读取数据并将渲染帧输出到磁盘。

只要从同一个内存位置读取两个或多个线程,就可以顺利运行。当其中至少一个写到共享内存中,而其他人正在从中读取时,麻烦就开始了。此时可能出现两个问题:
数据争用-当编写器线程修改内存时,读线程可能正在从内存中读取数据。如果编写器还没有完成它的工作,读卡器将得到损坏的数据;
竞态条件-读线程应该只有在写入之后才能读取。如果正好相反呢?比数据争用更微妙的是,争用条件是两个或多个线程以不可预知的顺序执行它们的工作,而实际上,操作应该按照正确的顺序执行。您的程序可以触发一个争用条件,即使它受到数据争用的保护。

如果一段代码工作正常,即没有数据争用或争用条件,即使许多线程同时执行它,也可以说它是线程安全的。您可能已经注意到一些编程库声明自己是线程安全的:如果您正在编写一个多线程程序,您希望确保任何其他第三方函数可以跨不同的线程使用,而不会触发并发问题。

数据竞争的根本原因

我们知道一个CPU内核一次只能执行一条机器指令。这种指令被称为原子指令,因为它是不可分割的:它不能被分解成更小的操作。

不可分割的属性使得原子操作本质上是线程安全的。当一个线程对共享数据执行原子写入时,没有其他线程可以读取修改的一半。相反,当一个线程对共享数据执行原子读取时,它会读取在某一时刻出现的整个值。线程无法通过原子操作,因此不会发生数据争用。

坏消息是绝大多数的操作都是非原子的。甚至一些硬件上的x=1这样的简单分配也可能由多个原子机器指令组成,使分配本身成为一个整体而非原子。因此,如果一个线程读取X而另一个线程执行分配,则会触发数据争用。

竞态条件的根本原因

抢占式多任务使操作系统能够完全控制线程管理:它可以根据高级调度算法启动、停止和暂停线程。作为程序员,您不能控制执行的时间或顺序。事实上,不能保证这样的简单代码:

writer_thread.start()
reader_thread.start()

将以该特定顺序启动两个线程。运行这个程序几次,您会注意到它在每次运行时的行为是不同的:有时编写器线程首先启动,有时读卡器启动。如果您的程序需要编写器始终在读者面前运行,那么您肯定会遇到竞争情况。

这种行为被称为不确定性:结果每次都会改变,你无法预测。调试受竞争条件影响的程序非常烦人,因为您不能总是以可控方式重现问题。

教线程相处:并发控制

同步-一种确保一次只有一个线程使用资源的方法。同步是将代码的特定部分标记为“受保护的”,这样两个或多个并发线程就不会同时执行它,从而破坏共享数据;

原子操作-借助操作系统提供的特殊指令,可以将一组非原子操作(如前面提到的分配)转换为原子操作。这样,无论其他线程如何访问共享数据,共享数据始终保持有效状态;

不可变数据-共享数据被标记为不可变,没有什么可以更改它:线程只能从中读取,消除了根本原因。正如我们所知,线程可以安全地从相同的内存位置读取,只要它们不修改它。这是函数式编程背后的主要原理。

原文链接

多线程的简单介绍(翻译)相关推荐

  1. python 协程 多线程_python进阶之多线程(简单介绍协程)

    多线程 线程:实现多任务的另一种方式 一个进程中,也经常需要同时做多件事,就需要同时运行多个'子任务',这些子任务,就是线程 线程又被称为轻量级进程(lightweight process),是更小的 ...

  2. gitee合并分支_使用Gitee进行协作翻译的简单介绍

    协作翻译的Gitee使用介绍 由于Github国内访问不稳定,加之大部分同学应该都在国内,所以我使用了Gitee进行协作.(github的操作也是完全一样的) 很多同学说不知道怎么创建分支,也不知道怎 ...

  3. 【软件开发】Java语言的简单介绍

    Java语言的简单介绍 一.Java语言的介绍 二.Java的版本 三.JDK的介绍 四.Java API文档 五.Java语言的特点 1. 面向对象 2. 解释性 3. 多线程 4. 可移植性 / ...

  4. Android 系统简单介绍

    Android 系统简单介绍 2011年11月20日 写这篇文章的目的是为了给那些刚刚入手安 卓手机的新手们一些参考,希望他们能快速的上手 [第一期]ANDROID基础知识1~20 [第二期]继续泡! ...

  5. Java虚拟机内存模型简单介绍

    一.虚拟机 同样的java代码在不同平台生成的机器码肯定是不一样的,因为不同的操作系统底层的硬件指令集是不同的. 不知道同学们还记不记得,在下载jdk的时候,我们在oracle官网,基于不同的操作系统 ...

  6. 《Real-Time Rendering 4th Edition》读书笔记--简单粗糙翻译 第二章 渲染管线 The Graphics Rendering Pipeline

    写在前面的话:因为英语不好,所以看得慢,所以还不如索性按自己的理解简单粗糙翻译一遍,就当是自己的读书笔记了.不对之处甚多,以后理解深刻了,英语好了再回来修改.相信花在本书上的时间和精力是值得的. -- ...

  7. Netty5的例子,简单介绍Netty的用法

    转自:http://blog.csdn.net/tjbsl/article/details/51038947 这是一个netty快速入门的例子,也是我的学习笔记,比较简单,翻译于官方的文档整理后把所有 ...

  8. 简单介绍JS与JSP的区别

    参考了一些网上的资料,总结了一下 1.JSP全称是java server page    JS全称是javaScript 2.最主要的区别是运行位置不同. JSP运行在后台服务器上,混合在HTML中的 ...

  9. JavaScript词法作用域的简单介绍

    by Michael McMillan 迈克尔·麦克米兰(Michael McMillan) JavaScript词法作用域的简单介绍 (An easy intro to Lexical Scopin ...

  10. HDFS简单介绍及用C语言訪问HDFS接口操作实践

    一.概述 近年来,大数据技术如火如荼,怎样存储海量数据也成了当今的热点和难点问题,而HDFS分布式文件系统作为Hadoop项目的分布式存储基础,也为HBASE提供数据持久化功能,它在大数据项目中有很广 ...

最新文章

  1. Oracle:递归查询(树形结构数据)
  2. Android, BaseAdapter 处理大数据量时的优化
  3. let finger cross
  4. 打印异常堆栈_定位生产问题时,异常堆栈莫名丢了,何解?
  5. 如何让主机合规分析报告评分达到90分?
  6. 配置LANMP环境(9)-- 安装Git与vsftp
  7. 一种用于茶叶病害识别的低阶学习方法
  8. linux shell脚本 可以全局使用
  9. setsebool命令和设置命令
  10. 初级Java程序员如何快速提升自己的能力?
  11. ips 测试软件,IPS测试方法.doc
  12. 时空本质性差异:两点间的距离不再是直线最短
  13. Latex取消英语单词自动断行
  14. 我看《三体》:行走于黑暗森林,常防备降维打击
  15. tagslam框架:LiDARTag和AprilTags,只使用特定标签的雷达/相机
  16. matlab 二维矩阵变成一维矩阵
  17. 【算法题】2309. 兼具大小写的最好英文字母
  18. c语言自制服务器之间调用文件夹,C语言实现一种简单的应用服务器内部数据结构的思路(三)...
  19. camtasia studio2022汉化屏幕录屏录像
  20. Android apk瘦身讲解

热门文章

  1. android识别人脸并放贴纸,超简单集成HMS ML Kit 人脸检测实现可爱贴纸
  2. 每日科创板之620:天淮科技注册生效 另有3家过会3家已问询
  3. XMPP-即时传输协议
  4. git清理认证信息和缓存数据
  5. React入门8-复合组件
  6. 【闲趣】可以自动编写论文、散文的智能AI
  7. 拾方易商业wifi能否成为物联网入口
  8. SparkContext 源码分析
  9. 13--微信小程序 修改班级名称(组件)按钮、input
  10. QT实现托盘图标,弹出气泡消息(类似微信托盘)