为什么多线程是个坏主意

标签: JAVA | 发表时间:2016-08-11 01:35 | 作者:danielhu
出处:http://ifeve.com

原文地址:

Unix编程艺术 中,提到了尽量避免多线程编程模型, 认为这样只会增加复杂度, 提倡使用多进程, 这样本质上就可以避免多线程『共享内存数据』产生的 “corruotped memory” 问题。

其中, 提到了一篇文章 Why Threads Are A Bad Idea, 对于多线程编程和事件编程分析的非常好, 具体的翻译如下:


1 介绍

线程的背景:
  • 在操作系统中出现多线程
  • 逐渐演变成 用户层面的编程工具
  • 被认为是多种问题的一种通用解决方案
  • 每一个程序员都需要成为 一个多线程编程的高手吗?
根本性的问题:

多线程的程序非常难以正确的编写!!!

替代性的方案:

使用事件驱动的编程方法

特别声明:
  • 对于大部分的多线程程序,使用事件驱动是一个更好的选择
  • 只有当使用CPU多核的时候, 才需要使用多线程编程

2 多线程的本质

多线程编程

  • 一般用来管理并发问题
  • 多个独立相互执行的任务
  • 共享的内存
  • 预先的安排机制(Pre-emptive scheduling)
  • 同步机制(synchronization)

3 多线程的用途

  • 操作系统: 对每一个用户进程分配一个内核线程
  • 科学应用程序: 每个CPU分配一个线程(对计算要求性很高的程序)
  • 分布式系统: 进程请求并行(同步记性的I/O操作)
  • GUIs程序
    • 线程对应用户的行为. 在长时间的后台计算过程中仍然可以处理图形展示
    • 多媒体, 动画方面的程序编写

4 多线程有什么问题?

痛苦指数

  • 对于一般的程序员而言,难以掌握。
  • 即使对于专家,多线程编程也是痛苦的。

5 为什么多线程编程很难?

  • Synchronization(同步机制):
    • 必须通过锁来共享数据
    • 忘记了加锁?就会导致受污染的数据
  • 死锁
    • 依赖锁,会导致循环依赖
    • 每个处理程序等待其他处理程序: 导致系统挂起

系统挂起

6 为什么多线程编程很难?

  • 难以调试: 因为 数据依赖,时间依赖
  • 线程破坏了抽象: 无法设计出模块化的程序
  • 因为锁导致回调无法完成

多线程

7 为什么多线程编程很难?

  • 很难达到非常好的性能
    • 简单的锁导致了低并发
    • 而精密的锁又会导致复杂度提升, 降低了一般情况下的性能
    • OSes限制了性能提升(调度, 环境切换)
  • 线程不受支持
    • 难以支持多线程代码(mac, windows)
    • 一些标志库不是线程安全的
    • 内核调用, windows系统不是多线程
    • 很少有多线程编程的调试工具
  • 通常不需要并发场景

8 时间驱动编程

  • 一个执行流进程: 没有CPU的并发
  • 在时间上注册消息(通过回调)
  • 事件轮询等待消息, 调用处理器模型
  • 时间处理器没有抢断
  • 处理器通常是 短生命周期的

事件驱动

9 事件驱动编程被用来干什么

  • 大多数的GUIs编程:
    • 一个处理器对应一个事件
    • 处理器用来执行行为(撤销,删除文件等)
  • 分布式系统
    • 一个处理器用来对应一个输入源
    • 处理进来的请求,返回结果
    • 事件驱动的I/O 来处理 I/O并发

10 事件驱动编程的问题

  • 长时间运行的时间处理器会导致 程序没有反应, 解决办法:
    • 对于长时间运行的程序Fork off子程序处理, 当处理结束后使用事件
    • 打断处理器执行(比如: 事件驱动的I/O)
    • 定期回调 时间处理器中的 事件循环
  • 通过处理器无法维护本地内存状态(处理器必须返回)
  • 没有CPU的并发(不太合适科学计算程序)
  • 事件驱动的编程并不总是被支持

11 多线程编程 VS 事件驱动编程

  • 事件驱动编发编程尽可能的避免 并发, 而多线程编程则倾向于并发:
    • 使用事件驱动编程更加容易: 不用考虑并发, 不用考虑抢占, 不用考虑同步和死锁
    • 只在特定的情况下,才使用复杂的技术栈
    • 使用多线程编程, 即使最简单的程序也需要面对很高的复杂度(full complexity)
  • 使用事件驱动更加容易调试
    • 事件驱动编程只和时间依赖有关, 不需要考虑内部的调度
    • 问题更加容易跟踪: 较慢的按钮点击反应 和 内存数据污染 时候, 前者问题更加容易定位

12 多线程编程 VS 事件驱动编程

  • 在单个CPU上时间驱动程序比线程更加快速
    • 没有锁的覆盖
    • 没有上下文环境的 切换
  • 事件驱动编程更加面向接口编程
  • 多线程提供了真正的并发性
    • 对于多CPU的机器来说,是可以扩展性能
    • 可以长时间的运行处理程序而不需要冻结

13 你需要放弃多线程吗?

  • 不需要的情况: 对于应该程序性能要求很高的服务(比如: 数据库服务器)
  • 但是, 尽可能的避免多线程编程:
    • 对于 GUIs程序, 分布式系统, 性能要求不高的, 使用事件编程, 不是多线程
    • 只有当真正的多核CPU并发需要使用到的时候,使用多线程编程
    • 当使用多线程编程的时候,将多线程编程模块与其他模块进行隔离, 保持大部分代码都是单线程模型

隔离多线程的模块:
隔离

14 总结

  • 并发从根本上是很难的, 尽可能的避免
  • 多线程比事件更加强大,但是这种强大的功能很少真正需要
  • 多线程编程比事件编程更加难以写出正确的代码, 只有真正的专家才能掌握
  • 将事件 编程当做基本的开发工具(对于GUIs 和 分布式系统)
  • 只有当性能要求很高的服务时候,才使用 多线程

 

原创文章,转载请注明: 转载自 并发编程网 – ifeve.com 本文链接地址: 为什么多线程是个坏主意


相关 [多线程 主意] 推荐:

为什么多线程是个坏主意

- - 并发编程网 - ifeve.com
Unix编程艺术 中,提到了尽量避免多线程编程模型, 认为这样只会增加复杂度, 提倡使用多进程, 这样本质上就可以避免多线程『共享内存数据』产生的 “corruotped memory” 问题. Why Threads Are A Bad Idea, 对于多线程编程和事件编程分析的非常好, 具体的翻译如下:.

Java Thread多线程

- - CSDN博客推荐文章
Java Thread多线程. Java 多线程例子1 小例子. super("zhuyong");//设置线程的名字,默认为“TestThread”. Java 多线程例子2 前台线程(用户线程) 后台线程(守护线程 ). 1,setDaemon(true)后就是后台线程(守护线程 ),反之就是前台线程(用户线程).

Java多线程之synchronized

- - CSDN博客推荐文章
这里通过三个测试类阐述了synchronized应用的不同场景. 首先是最基本的synchronized Method的使用.  * @see 概述:Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor) .  * @see 说明:当synchronized关键字修饰一个方法时,则该方法为同步方法 .

[转]GDB调试多线程

- - 小彰
GDB 多线程调试基本命令 实现简介 以及一个问题的解决. 一直对GDB多线程调试接触不多,最近因为工作有了一些接触,简单作点记录吧. 先介绍一下GDB多线程调试的基本命令. 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID. 切换当前调试的线程为指定ID的线程.

java多线程总结

- - Java - 编程语言 - ITeye博客
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口. 对于直接继承Thread的类来说,代码大致框架是:. class 类名 extends Thread{. * @author Rollen-Holt 继承Thread类,直接调用run方法.             System.out.println(name + "运行     " + i);.

[转]Oracle Parallel 多线程

- - Oracle - 数据库 - ITeye博客
对于一个大的任务,一般的做法是利用一个进程,串行的执行,如果系统资源足够,可以采用parallel技术,把一个大的任务分成若干个小的任务,同时启用n个进程/线程,并行的处理这些小的任务,这些并发的进程称为并行执行服务器(parallel executeion server),这些并发进程由一个称为并发协调进程的进程来管理.

Java多线程学习

- - CSDN博客编程语言推荐文章
  线程是一种轻量级的进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单. 即多个线程可以同时执行,就像有多条流水线一样,可以同时进行工作,是并发执行的.   程序是由进程组成的,进程是由线程组成的.

Java多线程(二)同步

- - CSDN博客编程语言推荐文章
如果你的java基础较弱,或者不大了解java多线程请先看这篇文章 java多线程(一)线程定义、状态和属性. 同步一直是java多线程的难点,在我们做android开发时也很少应用,但这并不是我们不熟悉同步的理由. 希望这篇文章能使更多的人能够了解并且应用java的同步. 在多线程的应用中,两个或者两个以上的线程需要共享对同一个数据的存取.

Lua 下实现抢占式多线程

- Coder(码农) - 云风的 BLOG
Lua 5.2 的开发进度可以回溯到 2010 年 1 月. 漫长的流程到今天已经快两年过去,终于等到了 beta 版. 我十分期待它可以在 2011 年内正式发布. 在这几经折腾的两年里,许多新特性企图挤进 5.2 版,又最终被否决. 当我们审视改进列表,似乎看不到太多耳目一新的东西. 但如果仔细阅读一下源代码,就会发现,大部分地方都重新实现过了,以配合这些表面上看起来不大的修改.

浅析C++多线程内存模型

- 2sin18 - 并行实验室 | Parallel Labs
注:本文发表于《程序员》2011年第6期并行编程专栏,略有删改. 在即将到来的C++1x标准中,一个重大的更新就是引入了C++多线程内存模型. 本文的主要目的在于介绍C++多线程内存模型涉及到的一些原理和概念,以帮助大家理解C++多线程内存模型的作用和意义. 顺序一致性模型(Sequential Consistency).