Python并发编程(2)——初始Python多线程

左手编程,右手年华。大家好,我是一点,关注我,带你走入编程的世界。
公众号:一点sir,关注领取python编程资料

前言

什么是多线程?

为什么需要多线程?

多线程的优点和缺点?

...

技术不是也不能成为空中楼阁,技术一定是要为需求服务的,什么有用,才去用什么。那么多线程对于编程人员来说,有用吗?肯定是有用,而且有大用,这是程序员从初级走向高级的重要标志之一。现在开始我们以世界上最流行的语言之一,Python为例,介绍一下多线程。

什么是多线程

多线程到底是什么呢?百科上面对于多线程的定义是指从软件或者硬件上实现多个线程并发执行的技术。多线程在计算机术语中也可以叫并发,指的是在单个系统里同时执行多个独立的活动,而不是顺序的一个接一个的执行。在多核CPU上,多线程甚至可以实现并行。关于并发和并行的区别,后期我也会输出。

大家要注意一点,在单核CPU上面,并不是真正的并行,而是通过时间片轮询的方式来看起来像是并行,真正的并行应该是在多核CPU上面。而在Python上,多线程并行目前为止只是一个美丽的梦。至于原因,后面会解释。

为什么需要多线程

为什么需要多线程?单线程编程做的好好的,又简单又好用,为什么要弄出一个多线程编程呢?难道前人是为了设计而设计了个多线程的?显然这是不可能,那么是什么原因呢?

假设这样的一个场景,假如你要计算1到10亿相加的和,如果用单线程,需要耗时一定的时间,这个就可以考虑用多线程,将10亿平均分成若干段,分别在和后,在将这几段相加,时间整体会比用单线程快的。

又比如有这样的一个场景,你在一个软件中要导入100万条的数据,如果你用单线程,那么你在导入的过程中,你只能等待它导完,其他什么事你都做不了,因为软件会卡在那里,之所以卡死在那里,是因为软件里面在传输数据,而你是用单线程,数据还没传输完,虎门的代码无法执行。所以这个时候你必须用多线程,开启一个线程来导入这100万条数据,这样才能保证在导入数据的同时,你还可以在软件上做其他操作。

所以用最精炼的语言概括无非就是以下两个原因。

效率和体验

效率主要体现在服务端,更多表现为对速度的追求。体验更多是体现在客户端,更多表现为界面交互。当然肯定不是说效率只体现在服务端,用户体验只体现在客户端,无论是效率还是用户体验在服务端和客户端上都会体现,只是说侧重点不一样。

多线程的优点和缺点

要精炼的概括出多线程的优点,无非就是从为什么需要多线程这个地方入手。为什么需要多线程?为了效率和体验!所以多线程的优点无非就是:

速度更快、体验更好。

而其他观点认为开销小,资源占用少,更加轻量级,无非就是与进程相比较比较的角度来看的,并不能算是多线程真正的优点。

世界是公平的,得到了好处,就一定会有牺牲。你觉得岁月静好,只不过有人替你负重前行罢了。多线程也是一样的!

那么多线程的缺点是啥呢?

数据容易出错。

这就是它最大的缺点!为什么数据容易出错呢?因为多线程将任务复杂化了,数据在多个线程中增删改查,一不小心就会出错,这也是没有办法的事情,因为这是为优点做出的牺牲。所以多线程编程需要更加小心,测试要更加详细。

当然,编程更加复杂也是一大缺点,像线程初始化,线程启动,线程传参等等,大家是不能用传统的函数调用来看待,内部的实现逻辑更是涉及到操作系统的相关知识。

多线程的实现

现在的高级编程语言几乎是都有对多线程的支持,只是方式可能不一样。Python 从早期版本开始就内置了对多线程的支持。通过 threading 模块,Python 允许开发者轻松地创建和管理线程。这使得 Python 程序能够并发处理,从而提升应用的性能和用户体验。 Python 实现多线程的方式也是非常简单的,以下是一个简单的例子:

import threading
import time

def thread_task(message):
    print("let's sleep some time")
    time.sleep(3)
    print(message)

def main():
    print("main thread start")
    t = threading.Thread(target=thread_task, args=("hello, sir",))
    t.start()
    t.join()
    print("main thread end")

if __name__ == "__main__":
    main()

在这个例子中,threading.Thread(target=thread_task) 创建了一个新的线程,该线程执行 thread_task 函数。t.start() 调用启动了子线程,而 t.join() 确保主线程等待子线程完成后再继续执行,这样可以保证程序的有序退出,而sleep几秒就是模拟这种情况。 thread_task 函数接受一个参数 message。在创建 Thread 对象时,我们通过 args 参数传递了一个元组 ("Hello, sir",) 给 thread_task 函数。注意,即使只有一个参数,也需要以元组的形式传递。

更多精彩内容,请关注同名公众:一点sir(alittle-sir)