首页 > 单线程和多线程的哪个简单?

单线程和多线程的哪个简单?

昨天在浏览一篇文章的时候,发现文章中有这么一句话:

(3)便于建模。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。

我对此表示难以理解,并且在回复中与作者展开了热烈的讨论,但似乎我们谁都无法说服谁。在我看来事情是很明白的,多线程的复杂度肯定是大于等于单线程的复杂度的,但作者却说多线程更简单。所以在这里提问,想了解一下各位的看法。

下面是我和作者“讨论/争论”的内容:

)这个讲反了吧。单线程的才简单,只是效率低;多线程会大幅度增加代码复杂度,但是会提高效率

作者)我举个例子吧,比方说一个支付系统,它要完成三项工作:1、根据用户的订单后台扣费 2、发送短信给用户提示用户消费成功 3、在某张表中记录用户此次消费行为,以便分析用户的消费习惯,引导用户消费,这是大数据时代会常做的一件事

假如说这三项工作每项工作都包含很多细节,放在单线程里面操作,那么这条线程要考虑的内容就非常多,整个代码流程会变得很复杂

如果我是用多线程就不一样了,我完全可以将此次的行为划分为三部分任务:1、扣费任务 2、发短信任务 3、记录用户消费任务。每部分任务一条线程执行,只需要保证发短信任务和记录用户消费任务在用户扣费任务成功之后就可以了。这三项任务可以分别交由三个不同的开发者进行开发,由于将一个大的任务拆分成了几个小的任务,因此对每部分小任务来说建模、整理流程就变得简单了。

再举个例子来说,一条线程统计C盘、D盘、E盘总共三个磁盘的内存大小,和三条线程分别统计C盘、D盘、E盘三个磁盘的大小并交由另一条线程进行汇总,明显后面的写起来会比前面的容易吧。

你说的单线程简单、多线程复杂,是从底层实现角度来说的,因为多线程需要考虑诸如线程切换这种问题,非常复杂,但是这个Java虚拟机已经帮助开发者实现好了。我说的单线程复杂、多线程简单,是从应用层角度来说的,大任务划分为小任务,便于开发和团队协作

)我说的多线程复杂就是针对我们开发者来说的!你所举的两个例子。第一个例子,为什么使用多线程反而简单呢?扣费任务、发短信任务、记录用户消费任务,把这些任务写成3个函数难道比使用多线程复杂?使用多线程也至少要写3个函数吧(实际上是3个类!)?而且多线程之间需要同步,例如后面2个任务必须等到扣费完成以后吧,单线程3个函数完全不用同步,顺序执行即可。而且你也说了“只需要保证发短信任务和记录用户消费任务在用户扣费任务成功之后就可以了”,说明你也考虑到同步问题了,而线程的同步问题是最复杂的一类问题,没有简单的解决方案,尤其是具有复杂逻辑的多线程之间的同步,经常会导致同步失败,以及死锁问题。而你却说多线程简单!

第二个例子我就更难理解了,统计磁盘大小的程序,逻辑最简单的应该是在一个线程里面顺序调用函数进行统计吧?程序执行完了结果立即就出来了。

作者)首先,你要知道Java应用如此广泛,有两点原因:1、面向对象 2、跨平台性。其中第二点是重点,第一点是次重点。你反复提到了函数,这首先就不符合面向对象的思路,面向对象分析针对的是对象,面向过程分析针对的才是函数。当然,很多问题,我们都可以用函数的思路去解决,A()-->B()-->C(),但这样,如果对于一个问题的思考,都从你说的"函数"角度出发,就失去了面向对象的特点,问题越大,就越增加了分析问题的复杂度。

其次,你提到了同步,我当时没有把这个点讲清楚,实际上单线程编程才需要考虑同步,多线程根本就不需要考虑同步的问题。"只需要保证发短信任务和记录用户消费任务在用户扣费任务成功之后就可以了",怎么保证?使用异步架构(也就是多线程),生产者/消费者模型的问题就可以了。每次扣费成功,往一个阻塞队列里面放一个数据,发短信任务、记录用户消费任务只需要往这个阻塞队列里面取数据就可以了,只要队列里面有数据,就表示这是处理成功的扣费任务,这样三个任务可以相互独立发展、不需要相互依赖,是不是就各自建模了呢?

至于最后一段话,看得出来,你对这个问题的解读太简单了,"逻辑最简单的应该是在一个线程里面顺序调用函数进行统计吧",这是典型的、纯粹的面向过程的想法,前面我已经提过了。

说了这么多,总结两点:1、假如面向过程好,就不会有面向对象诞生。小问题面向过程方便,问题越大越需要使用面向对象的思路去分析问题。你的说法,都是建立在任务比较简单的前提下的,没有真正考虑到一个任务的复杂度 2、使用多线程,对大任务进行拆分,使用异步架构解耦,各自独立发展,便于建模,这是使用多线程的优势所在

)“单线程编程才需要考虑同步,多线程根本就不需要考虑同步的问题”,请问你是如何得出这么骇人听闻、惊天地泣鬼神的结论的?(没有讽刺的意思,这是我看到你这句话时的真实反应)。

另外,我所说的函数和面向过程还是面向对象没有任何关系,无论是面向过程还是面向对象,最后都需要函数(方法)来处理事情,不是吗?你把我所说的单方面理解成面向过程是不对的,当然,也许是我没有说清楚。但这里我们讨论的重点不是这个。

但是这里我要强调的一点是,无论是采用面向过程还是面向对象的方式,都与我们这里讨论的重点——多线程——没有任何关系。无论是面向对象也好,面向过程也罢,对你所举的例子来说,单线程无疑都是比多线程实现起来要更简单。实际上对于所有的例子都是如此,多线程的复杂度>=单线程的复杂度,在任何情况下都成立,而且通常都是大于,只有在特殊情况下才有可能等于(多个线程之间完全没有关系,是完全独立的)。

最后,其他的我想我们不必再讨论了,毕竟不是你这篇文章的重点,以后有机会可以再交流。但是希望你能够就我开头所引用的那句话给予回复,为什么你会说“单线程编程才需要考虑同步,多线程根本就不需要考虑同步的问题”这种话呢?

)“使用多线程,对大任务进行拆分,使用异步架构解耦,各自独立发展,便于建模,这是使用多线程的优势所在”

解耦有多种方法,没必要完全依赖于异步架构。另外你只考虑到解耦这一层,你考虑过异步架构的调试、排错和Bug重现吗?异步架构最容易出现的问题就是“幽灵Bug”,也就是那些偶尔出现一次,等到你真正想找出它的时候它可能死也不出现!但是放到生产环境之后保不住哪天又给你来几次,如果你真的对异步架构很了解,不可能不知道它的弊端吧?

异步架构确实能够有效地在模块间解耦,但它是以增加复杂度为代价的。所以NodeJS采用异步架构的同时,将用户逻辑限定到单线程中,事实证明这是一种很好的策略!想一想如果NodeJS允许多线程会怎么样?

(以上)


简单看了下,貌似两者不在一个出发点,或者说说的压根不是一件事。
单线程简单是简单在程序编写上,复杂在业务逻辑
多线程简单是简单在通过分拆将复杂的业务逻辑分拆到多个简单的子任务,这个其实不是多线程的概念,LZ所说的作者有点跑偏了,单线程也是可以分拆呀。当然多线程的复杂就不用说了,同步、竞争要多麻烦有多麻烦。


自己的观点,有错欢迎点评


嗯,在你的推荐下看到了这个讨论内容,其实我觉得总体来说是明确的,我在这里总结下。

我说的多线程简单,单线程复杂:大任务拆分成小任务,对小任务建模、编码简单
你说的多线程复杂,单线程简单:因为将系统应用在高并发下,多线程的方式将会出现很多难以预期的BUG,也难以定位、修改

我不认为大任务拆分成小任务非要使用多线程,单线程的方式也可以,只是我提出了一个观点:单线程拆分的逻辑太多,函数太多,就把面向对象编程编程了面向过程编程了,这并不符合Java语言的特点。

最后回到原文,因为那条的主题是"多线程的优点",只要多线程的方式可以将大任务拆分成小任务,并且对小任务分别建模,那就可以印证那一条优点的正确性了。

说了这么多,关键还是我们讨论问题的出发点不一致,就像楼上说的,从出发点的角度考虑,在开发难度和性能间取舍。


这还用讨论, 必然是单线程简单啊. 多线程带来的好处是, 很可能能提高系统的并发量或者吞吐量. 这里为什么用很可能能, 意思是, 很多时候都是妄想.
比如我在做Server开发, 看到有一些人用多线程写逻辑, 实际上并不能提高系统的并发量和吞吐量. 反而使问题复杂化, 甚至有可能并行会退化到1, 可能还不如单线程程序.


我觉得看出发角度是什么,只能从开发难度跟性能中取舍

【热门文章】
【热门文章】