百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

Python 多线程编程 python多线程例子

yuyutoo 2024-10-12 01:03 3 浏览 0 评论

人生苦短,我用 Python!

我们知道,多线程与单线程相比,可以提高 CPU 利用率,加快程序的响应速度。

单线程是按顺序执行的,比如用单线程执行如下操作:

6秒读取文件1

9秒处理文件1

5秒读取文件2

8秒处理文件2

总共用时 28 秒,如果开启两条线程来执行上面的操作(假设处理器为多核 CPU),如下所示:

6秒读取文件1 + 5秒读取文件2

9秒处理文件1 + 8秒处理文件2

只需 15 秒就可完成。

1 线程与进程

1.1 简介

说到线程就不得不提与之相关的另一概念:进程,那么什么是进程?与线程有什么关系呢?简单来说一个运行着的应用程序就是一个进程,比如:我启动了自己手机上的酷猫音乐播放器,这就是一个进程,然后我随意点了一首歌曲进行播放,此时酷猫启动了一条线程进行音乐播放,听了一部分,我感觉歌曲还不错,于是我按下了下载按钮,此时酷猫又启动了一条线程进行音乐下载,现在酷猫同时进行着音乐播放和音乐下载,此时就出现了多线程,音乐播放线程与音乐下载线程并行运行,说到并行,你一定想到了并发吧,那并行与并发有什么区别呢?并行强调的是同一时刻,并发强调的是一段时间内。线程是进程的一个执行单元,一个进程中至少有一条线程,进程是资源分配的最小单位,线程是 CPU 调度的最小单位。

线程一般会经历新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead)5 种状态,当线程被创建并启动后,并不会直接进入运行状态,也不会一直处于运行状态,CPU 可能会在多个线程之间切换,线程的状态也会在就绪和运行之间转换。

1.2 Python 中的线程与进程

Python 提供了 _thread(Python3 之前名为 thread ) 和 threading 两个线程模块。_thread 是低级、原始的模块,threading 是高级模块,对 _thread 进行了封装,增强了其功能与易用性,绝大多数时候,我们只需使用 threading 模块即可。下一节我们会对 threading 模块进行详细介绍。

Python 提供了 multiprocessing 模块对多进程进行支持,它使用了与 threading 模块相似的 API 产生进程,除此之外,还增加了新的 API,用于支持跨多个输入值并行化函数的执行及跨进程分配输入数据,详细用法可以参考官方文档 https://docs.python.org/zh-cn/3/library/multiprocessing.html。

2 GIL

要说 Python 的多线程,必然绕不开 GIL,可谓成也 GIL 败也 GIL,到底 GIL 是啥?怎么来的?为什么说成也 GIL 败也 GIL 呢?下面就带着这几个问题,给大家介绍一下 GIL。

2.1 GIL 相关概念

GIL 全称 Global Interpreter Lock(全局解释器锁),是 Python 解释器 CPython 采用的一种机制,通过该机制来控制同一时刻只有一条线程执行 Python 字节码,本质是一把全局互斥锁,将并行运行变成串行运行。

什么是 CPython 呢?我们从 Python 官方网站下载安装 Python 后,获得的官方解释器就是 CPython,因其是 C 语言开发的,故名为 CPython,是目前使用最广泛的 Python 解释器;因为我们大部分环境下使用的默认解释器就是 CPython,有些人会认为 CPython 就是 Python,进而以为 GIL 是 Python 的特性,其实 CPython 只是一种 Python 解释器,除了 CPython 解释器还有:PyPy、Psyco、Jython (也称 JPython)、IronPython 等解释器,其中 Jython 与 IronPython 分别采用 Java 与 C# 语言实现,就没有采用 GIL 机制;而 GIL 也不是 Python 特性,Python 可以完全独立于 GIL 运行。

2.2 GIL 起源与发展

我们已经知道了 GIL 是 CPython 解释器中引入的机制,那为什么 CPython 解释器中要引入 GIL 呢?GIL 一开始出现是因为 CPython 解释器的内存管理不是线程安全的,也就是采用 GIL 这把锁解决 CPython 的线程安全问题。

随着时间的推移,计算机硬件逐渐向多核多线程方向发展,为了更加充分的利用多核 CPU 资源,各种编程语言开始对多线程进行支持,Python 也加入了其中,尽管多线程的编程方式可以提高程序的运行效率,但与此同时也带来了线程间数据一致性和状态同步的问题,解决这个问题最简单的方式就是加锁,于是 GIL 这把锁再次登场,很容易便解决了这个问题。

慢慢的越来越多的代码库开发者开始接受了这种设定,进而开始大量依赖这种特性,因为默认加了 GIL 后,Python 的多线程便是线程安全的了,开发者在实际开发无需再考虑线程安全问题,省掉了不少麻烦。

对于 CPython 解释器中的多线程程序,为了保证多线程操作安全,默认使用了 GIL 锁,保证任意时刻只有一个线程在执行,其他线程处于等待状态。

2.3 成也 GIL,败也 GIL

以前为了解决多线程的线程操作安全问题,CPython 采用了 GIL 锁的方式,这种方式虽然解决了线程操作安全问题,但由于同一时刻只能有一条线程执行,等于主动放弃了线程并行执行的机会,因此在目前 CPython 下的多线程并不是真正意义上的多线程。

现在这种情况,我们可能会想要实现真正意义上的多线程,可不可以去掉 GIL 呢?答案是可以的,但是有一个问题:依赖这个特性的代码库太多了,现在已经是尾大不掉了,使去除 GIL 的工作变得举步维艰。

当初为了解决多线程带来的线程操作安全问题使用了 GIL,现在又发现 GIL 方式下的多线程比较低效,想要去掉 GIL,但已经到了尾大不掉的地步了,真是成也 GIL,败也 GIL。

对于 CPython 下多线程的低效问题,除了去掉 GIL,还有什么其他解决方案吗?我们来简单了解下:

1)使用无 GIL 机制的解释器;如:Jython 与 IronPython,但使用这两个解释器失去了利用 C 语言模块一些优秀特性的机会,因此这种方式还是比较小众。

2)使用 multiprocess 代替 threading;multiprocess 使用了与 threading 模块相似的 API 产生进程,不同之处是它使用了多进程而不是多线程,每个进程有自己独立的 GIL,因此不会出现进程之间的 GIL 争抢,但这种方式只对计算密集型任务有效,通过后面的示例我们也能得出这个结论。

3 多线程实现

_thread 模块是一个底层模块,功能较少,当主线程运行完毕后,如果不做任何处理,会立刻把子线程给结束掉,现实中几乎很少使用该模块,因此不作过多介绍。对于多线程开发推荐使用 threading 模块,这里我们简单了解下通过该模块实现多线程,详细介绍我们放在了下一节多线程的文章中。

threading 模块通过 Thread 类提供对多线程的支持,首先,我们要导入 threading 中的类 Thread,示例如下:

from threading import Thread

依赖导入了,接下来要就要创建线程了,直接创建 Thread 实例即可,示例如下:

# method 为线程要执行的具体方法

p1 = Thread(target=method)

若要实现两条线程,再创建一个 Thread 实例即可,示例如下:

p2 = Thread(target=method)

需要实现更多条的线程也是一个道理。线程创建好了,通过 start 方法启动即可,示例如下:

p1.start()

p2.start()

如果是多线程任务,我们可能需要等待所有线程执行完成再进行下一步操作,使用 join 方法即可。示例如下:

# 等待线程 p1、p2 都执行完

p1.join()

p2.join()

4 多进程实现

Python 的多进程通过 multiprocessing 模块的 Process 类实现,它的使用基本与 threading 模块的 Thread 类一致,因此这里就不一步步说了,直接看示例:

# 导入 Process

from multiprocessing import Process

# 创建两个进程实例:p1、p2,method 是要执行的具体方法

p1 = Process(target=method)

p2 = Process(target=method)

# 启动两个进程

p1.start()

p2.start()

# 等待进程 p1、p2 都执行完

p1.join()

p2.join()

5 效率大比拼

现在我们已经了解了 Python 线程和进程的基本使用,那么 Python 单线程、多线程、多进程的实际工作效率如何呢?下面我们就以计算密集型和 I/O 密集型两种任务考验一下它们。

5.1 计算密集型任务

计算密集型任务的特点是要进行大量的计算,消耗 CPU 资源,比如:计算圆周率、对视频进行解码 ... 全靠 CPU 的运算能力,下面看一下单线程、多线程、多进程的实际耗时情况。

1)单线程就是一条线程,我们直接以主线程为例,来看下单线程表现如何:

# 计算密集型任务-单线程

import os,time

def task():

ret = 0

for i in range(100000000):

ret *= i

if __name__ == '__main__':

print('本机为',os.cpu_count(),'核 CPU')

start = time.time()

for i in range(5):

task()

stop = time.time()

print('单程耗时 %s' % (stop - start))

# 测试结果:

'''

本机为 4 核 CPU

单线程耗时 23.19068455696106

'''

2)来看多线程表现:

# 计算密集型任务-多线程

from threading import Thread

import os,time

def task():

ret = 0

for i in range(100000000):

ret *= i

if __name__ == '__main__':

arr = []

print('本机为',os.cpu_count(),'核 CPU')

start = time.time()

for i in range(5):

p = Thread(target=task)

arr.append(p)

p.start()

for p in arr:

p.join()

stop = time.time()

print('多线程耗时 %s' % (stop - start))

# 测试结果:

'''

本机为 4 核 CPU

多线程耗时 25.024707317352295

'''

3)来看多进程表现:

# 计算密集型任务-多进程

from multiprocessing import Process

import os,time

def task():

ret = 0

for i in range(100000000):

ret *= i

if __name__ == '__main__':

arr = []

print('本机为',os.cpu_count(),'核 CPU')

start = time.time()

for i in range(5):

p = Process(target=task)

arr.append(p)

p.start()

for p in arr:

p.join()

stop = time.time()

print('计算密集型任务,多进程耗时 %s' % (stop - start))

# 输出结果

'''

本机为 4 核 CPU

计算密集型任务,多进程耗时 14.087027311325073

'''

通过测试结果我们发现,在 CPython 下执行计算密集型任务时,多进程效率最优,多线程还不如单线程。

5.2 I/O 密集型任务

涉及到网络、磁盘 I/O 的任务都是 I/O 密集型任务,这类任务的特点是 CPU 消耗很少,任务的大部分时间都在等待 I/O 操作完成(因为 I/O 的速度远远低于 CPU 和内存的速度)。通过下面例子看一下耗时情况:

1)来看单线程表现:

# I/O 密集型任务-单线程

import os,time

def task():

f = open('tmp.txt','w')

if __name__ == '__main__':

arr = []

print('本机为',os.cpu_count(),'核 CPU')

start = time.time()

for i in range(500):

task()

stop = time.time()

print('I/O 密集型任务,多进程耗时 %s' % (stop - start))

# 输出结果

'''

本机为 4 核 CPU

I/O 密集型任务,单线程耗时 0.2964005470275879

'''

2)来看多线程表现:

# I/O 密集型任务-多线程

from threading import Thread

import os,time

def task():

f = open('tmp.txt','w')

if __name__ == '__main__':

arr = []

print('本机为',os.cpu_count(),'核 CPU')

start = time.time()

for i in range(500):

p = Thread(target=task)

arr.append(p)

p.start()

for p in arr:

p.join()

stop = time.time()

print('I/O 密集型任务,多进程耗时 %s' % (stop - start))

# 输出结果

'''

本机为 4 核 CPU

I/O 密集型任务,多线程耗时 0.24960064888000488

'''

3)来看多进程表现:

# I/O 密集型任务-多进程

from multiprocessing import Process

import os,time

def task():

f = open('tmp.txt','w')

if __name__ == '__main__':

arr = []

print('本机为',os.cpu_count(),'核 CPU')

start = time.time()

for i in range(500):

p = Process(target=task)

arr.append(p)

p.start()

for p in arr:

p.join()

stop = time.time()

print('I/O 密集型任务,多进程耗时 %s' % (stop - start))

# 输出结果

'''

本机为 4 核 CPU

I/O 密集型任务,多进程耗时 21.05265736579895

'''

通过 I/O 密集型任务在 CPython 下的测试结果我们发现:多线程效率优于多进程,单线程与多线程效率接近。

对于一个运行的程序来说,随着 CPU 的增加执行效率必然会有所提高,因此大多数时候,一个程序不会是纯计算或纯 I/O,所以我们只能相对的去看一个程序是计算密集型还是 I/O 密集型。

本节给大家介绍了 Python 多线程,让大家对 Python 多线程现状有了一定了解,能够根据任务类型选择更加高效的处理方式。

作者:纯洁的微笑

来源:微信公众号

相关推荐

野路子科技!2步教你把手机改造成一个FTP服务器,支持PC互传

哈喽,大家好,我是野路子科技,今天来给大家带来一个教程,希望大家喜欢。正如标题所言,就是教大家如何把售价改造成FTP服务器,而这个时候估计有朋友会问了,把手机改造成FTP服务器有什么用呢?现在有Q...

不得不看:别样于Server-U的群晖文件存储服务器的搭建与使用

我先前的作品中,有着关于Server-U的ftp文件存储服务器的搭建与访问的头条文章和西瓜视频,而且我们通过各种方式也给各位粉丝介绍了如何突破局域网实现真正意义上的公网访问机制技术。关于Server-...

Qt三种方式实现FTP上传功能_qt引入qftp库

FTP协议FTP的中文名称是“文件传输协议”,是FileTransferProtocol三个英文单词的缩写。FTP协议是TCP/IP协议组中的协议之一,其传输效率非常高,在网络上传输大的文件时,经...

Filezilla文件服务器搭建及客户端的使用

FileZilla是一个免费开源的FTP软件,分为客户端版本和服务器版本,具备所有的FTP软件功能。可控性、有条理的界面和管理多站点的简化方式使得Filezilla客户端版成为一个方便高效的FTP客户...

美能达柯美/震旦复印机FTP扫描怎么设置?

好多网友不知道怎么安装美能达/震旦复印机扫描,用得最多是SMB和FTP扫描,相对于SMB来说,FTP扫描安装步骤更为便捷,不容易出问题,不需要设置文件夹共享,所以小编推荐FTP来扫描以美能达机器为例详...

CCD(简易FTP服务器软件)_简单ftp服务器软件

CCD简易FTP服务器软件是一款很方便的FPT搭建工具,可以将我们的电脑快速变成一个FPT服务器。使用方法非常简单,只要运行软件就会自动生效,下载银行有该资源。该工具是不提供操作界面的,其他用户可以输...

Ubuntu系统搭建FTP服务器教程_ubuntu架设服务器

在Ubuntu系统上搭建FTP服务器是文件传输的一个非常实用方法,适合需要进行大量文件交换的场景。以下是一步步指导,帮助您在Ubuntu上成功搭建FTP服务器。1.安装vsftpd软件...

理光FTP扫描设置教程_理光ftp扫描设置方法

此教程主要用来解决WIN10系统下不能使用SMB文件夹扫描的问题,由于旧的SMB协议存在安全漏洞,所以微软在新的系统,WIN8/WIN10/SERVER201220162018里使用了新的SMB传...

纯小白如何利用wireshark学习网络技术

写在前面工欲善其事必先利其器!熟悉掌握一种神器对以后的工作必然是有帮助的,下面我将从简单的描述Wireshark的使用和自己思考去写,若有错误或不足还请批评指正。...

京东买13盘位32GB内存NAS:NAS系统安装设置教程

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:yasden你没有看错,我在京东自营商城购买硬件,组装了一台13盘位,32GB内存的NAS,硬盘有13个盘位!CPU是AMD的5500!本文...

FileZilla搭建FTP服务器图解教程_filezilla server搭建ftp服务器

...

python教程之FTP相关操作_python ftps

ftplib类库常用相关操作importftplibftp=ftplib.FTP()ftp.set_debuglevel(2)#打开调试级别2,显示详细信息ftp.connect(“I...

xftp怎么用,xftp怎么用,具体使用方法

Xftp是一款界面化的ftp传输工具,用起来方便简单,这里为大家分享下Xftp怎么使用?希望能帮到有需要的朋友。IIS7服务器管理工具可以批量管理、定时上传下载、同步操作、数据备份、到期提醒、自动更新...

树莓派文件上传和下载,详细步骤设置FTP服务器

在本指南中,详细记录了如何在树莓Pi上设置FTP。设置FTP可以在网络上轻松地将文件传输到Pi上。FTP是文件传输协议的缩写,只是一种通过网络在两个设备之间传输文件的方法。还有一种额外的方法,你可以用...

win10电脑操作系统,怎么设置FTP?windows10系统设置FTP操作方法

打印,打印,扫描的日常操作是每一个办公工作人员的必需专业技能,要应用FTP作用扫描文件到电脑上,最先要必须一台可以接受文件的FTP服务器。许多软件都需要收费标准进行,但人们还可以应用Windows的系...

取消回复欢迎 发表评论: