首页 > 编程语言 >听说Python有鸡肋?一起聊聊...

听说Python有鸡肋?一起聊聊...

时间:2023-04-03 23:34:48浏览次数:48  
标签:... processes 示例 Python num 线程 聊聊 进程 多线程

听说是鸡肋

一直以来,关于Python的多线程和多进程是否是鸡肋的争议一直存在,今晚抽空谈谈我的看法,以下是我的观点:

对于多线程:

Python 的多线程库 threading 在某些情况下确实是鸡肋的,这是因为 Python 的全局解释器锁(Global Interpreter Lock, GIL)导致了多线程的并发性能不能真正发挥出来。简单来说,这意味着在任何给定时刻只有一个线程能够真正地运行 Python 代码,这就限制了多线程的性能。

然而,对于一些特定类型的任务,比如 I/O 密集型的任务,多线程还是可以带来性能提升的。这是因为 I/O 操作通常会导致线程阻塞,让其他线程得以运行。此外,在 Python3 中,对于一些特殊情况,比如使用 asyncio 库,也可以通过协程实现并发执行,从而规避 GIL 的限制。

对于多进程:

Python 的多进程库 multiprocessing 是可以真正发挥出多核处理器的性能的,因为每个进程都有自己的解释器和 GIL。这意味着每个进程可以独立地运行 Python 代码,从而实现真正的并行处理。

当然,多进程也有一些缺点,比如进程之间的通信和数据共享比较麻烦。此外,每个进程的启动和销毁都会涉及到一定的开销,因此如果任务很小,多进程可能反而会带来性能下降。

多线程和多进程怎么选

对于不同类型的任务,多线程和多进程都有它们的优缺点,需要根据具体情况进行选择。如果你要处理的任务是 CPU 密集型的,那么多进程可能是更好的选择;如果是 I/O 密集型的,那么多线程可能更合适。

实战验证

  1. 下面我写一个简单的代码示例,用来说明 Python 多线程在 CPU 密集型任务中的性能问题:
import threading

counter = 0

def worker():
    global counter
    for i in range(10000000):
        counter += 1

threads = []
for i in range(4):
    t = threading.Thread(target=worker)
    threads.append(t)

for t in threads:
    t.start()

for t in threads:
    t.join()

print(counter)

这个代码示例定义了一个全局变量 counter,然后创建了 4 个线程,每个线程都会执行一个简单的循环,将 counter 的值加 1。最后输出 counter 的值。

在单线程模式下,循环完成后 counter 的值应该是 40000000,但是在多线程模式下,由于 GIL 的限制,多个线程并不能真正并行地执行代码,导致 counter 的最终值小于 40000000。例如,在我的机器上运行这个代码示例,最终的输出结果可能是 36092076,远小于预期的值。

这个示例表明,在一些 CPU 密集型的任务中,Python 多线程的性能受到 GIL 的限制,不能真正地发挥出多核处理器的优势。

  1. 我再写一个简单的代码示例,用来说明在 I/O 密集型任务中,多线程可以带来性能提升的情况:
import threading
import requests

urls = [
    "https://www.google.com",
    "https://www.baidu.com",
    "https://www.github.com",
    "https://www.python.org"
]

def worker(url):
    res = requests.get(url)
    print(f"{url} : {len(res.content)} bytes")

threads = []
for url in urls:
    t = threading.Thread(target=worker, args=(url,))
    threads.append(t)

for t in threads:
    t.start()

for t in threads:
    t.join()

这个代码示例创建了 4 个线程,每个线程负责访问一个 URL 并打印出该 URL 返回的内容长度。由于访问 URL 的操作是 I/O 密集型的,因此线程在等待服务器响应时会阻塞,让其他线程有机会执行。

在我的机器上运行这个代码示例,可以看到 4 个线程几乎同时执行,并在几乎相同的时间内完成了任务,证明了多线程在 I/O 密集型任务中的性能优势。

对于 Python3 中的 asyncio 库,它提供了基于协程的并发执行模型,可以在一定程度上规避 GIL 的限制。下面写了一个简单使用 asyncio 库的代码示例:

import asyncio
import aiohttp

urls = [
    "https://www.google.com",
    "https://www.baidu.com",
    "https://www.github.com",
    "https://www.python.org"
]

async def worker(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            content = await response.read()
            print(f"{url} : {len(content)} bytes")

async def main():
    tasks = []
    for url in urls:
        task = asyncio.create_task(worker(url))
        tasks.append(task)
    await asyncio.gather(*tasks)

asyncio.run(main())

这个代码示例使用 asyncio 库来实现异步访问多个 URL,其中 worker 函数是一个异步函数,使用 aiohttp 库发送异步 HTTP 请求。在主函数中,使用 asyncio.create_task 创建多个协程任务,并使用 asyncio.gather 函数等待所有协程任务完成。

在我的机器上运行这个代码示例,可以看到几乎同时访问 4 个 URL,并在几乎相同的时间内完成了任务,证明了 asyncio 库在 I/O 密集型任务中的性能优势。

  1. 我们继续看多进程,下面我写了一个简单的代码示例,用来说明 multiprocessing 库是否可以真正发挥出多核处理器的性能:
import multiprocessing

def worker(start, end):
    for i in range(start, end):
        print(i * i)

if __name__ == '__main__':
    processes = []
    num_processes = 4
    num_tasks = 20

    for i in range(num_processes):
        start = i * num_tasks // num_processes
        end = (i + 1) * num_tasks // num_processes
        p = multiprocessing.Process(target=worker, args=(start, end))
        processes.append(p)

    for p in processes:
        p.start()

    for p in processes:
        p.join()

这个代码示例创建了 4 个进程,每个进程负责计算一段整数的平方并打印出结果。由于每个进程有自己的解释器和 GIL,因此每个进程可以独立地运行 Python 代码,从而实现真正的并行处理。

在我的机器上运行这个代码示例,可以看到 4 个进程几乎同时执行,并在几乎相同的时间内完成了任务,证明了 multiprocessing 库可以真正发挥出多核处理器的性能。

  1. 之前提到,多进程在处理小任务时可能会带来性能下降,下面我写了一个简单的代码示例,说明以下这种情况:
import multiprocessing

def worker(num):
    result = num * num
    print(result)

if __name__ == '__main__':
    processes = []
    num_processes = 4

    for i in range(num_processes):
        p = multiprocessing.Process(target=worker, args=(i,))
        processes.append(p)

    for p in processes:
        p.start()

    for p in processes:
        p.join()

这个代码示例创建了 4 个进程,每个进程负责计算一个整数的平方并打印出结果。由于任务非常小,每个进程的计算时间非常短,因此进程的启动和销毁所涉及的开销可能会成为性能的瓶颈。

在我的机器上运行这个代码示例,可以看到进程的启动和销毁所涉及的开销导致整个程序的运行时间远远超过了单进程的运行时间。这说明在处理小任务时,多进程可能会带来性能下降,因此需要根据实际情况选择合适的并发处理方式。

最后的总结

Python 的并发编程有多种实现方式,包括多线程、多进程和协程等。其中,多线程通常适用于 I/O 密集型的任务,但由于 GIL 的存在,不能真正发挥出多核处理器的性能;而多进程则可以真正发挥出多核处理器的性能,但进程之间的通信和数据共享比较麻烦,每个进程的启动和销毁也会涉及到一定的开销。协程则是一种轻量级的并发处理方式,适用于 I/O 密集型任务和部分计算密集型任务,可以通过 async/await 关键字和 asyncio 库来实现。

在实际编程中,需要根据任务类型、数据量、机器配置等因素来选择合适的并发处理方式。对于小型任务,多进程可能会带来性能下降;对于计算密集型任务,可以考虑使用多进程或者协程;对于 I/O 密集型任务,可以使用多线程、多进程或者协程等方式。同时,还需要注意并发处理带来的数据竞争、死锁、线程安全等问题,以保证程序的正确性和性能。

本文转载于WX公众号:不背锅运维(喜欢的盆友关注我们):https://mp.weixin.qq.com/s/XMKKUXZxZwVd_PKevkdxaw

标签:...,processes,示例,Python,num,线程,聊聊,进程,多线程
From: https://www.cnblogs.com/ttropsstack/p/17284916.html

相关文章

  • 利用Python写入CSV文件的方法
    利用Python写入CSV文件的方法  #!/usr/bin/envpython#_*_coding:utf-8_*_importcsvcsvfile=file('test.csv','wb')csvfile.write(u'\ufeff'.encode('utf8'))writer=csv.writer(csvfile)writer.writerow(['id&......
  • Window下,利用Anaconda2创建jupyter-notebook的python3环境方法
    转载自:https://www.cnblogs.com/ljy2013/p/8351067.html随着深度学习的火热,越来越多的人去学习和了解这门技术。而做算法的同学为了能够更快,更高效的写出相关的深度学习算法出来,需要比较方便的开发环境。今天主要介绍一下在jupyternotebook中,新增python3的环境,从而可以使用tenso......
  • python+playwright 学习-45 drag_to 拖拽操作
    前言按住元素从页面的一个位置拖动到另外一个位置,可以用drag_to()方法实现拖拽操作场景目标元素拖动到指定位置drag_to拖拽操作您可以使用locator.drag_to()执行拖放操作。此方法将:将鼠标悬停在要拖动的元素上。按鼠标左键。将鼠标移动到将接收放置的元素。松开鼠......
  • python3.8下载安装
    https://www.python.org/downloads/windows/打开官网下载python   下好之后安装        ......
  • [oeasy]python0124_Code_page_437_IBM_5150_点阵式字形码_显示器效果
    字符显示器回忆上次内容简体和繁体的汉字字符数量都超级大感谢王选和陈堃銶等前辈发明了激光照排技术中文排版从此使用上了gb2312编码 ​ 添加图片注释,不超过140字(可选) 纸张之外显示器是更先进的输出设备 计算机是......
  • Python的web框架自动生成readme文件的第三方模块介绍以及使用方法
    第一步安装pipinstalldjango-readme-generator第二步在Django项目配置文件文件中的INSTALLED_APPS配置中注册一下INSTALLED_APPS=[ django_readme_generator,]第三步命令运行产生readme文件pythonmanage.pygenerate_readme其实很简单,以上三步可以轻轻松松的自动......
  • 学习python第二天
    流程控制一.顺序流程自上而下的顺序一条一条的执行代码。二.选择流程/分支流程1.1单分支ifif表达式:要执行的操作2.2双分支ifelseif表达式:要执行的操作else:否则要执行的操作2.3多分支ifelifelifelse条件表达式:三:循环流程while条件表达式: lie=9whileli......
  • Python使用Matplotlib画以日期为X轴的图
    Python使用Matplotlib画以日期为X轴的图步骤:用pd把字符串格式的日期转成date格式.使用 AutoDateLocator 设置x轴的属性.1frommatplotlibimportpyplotasplt2importpandasaspd3frommatplotlib.datesimport(4MonthLocator,5AutoDateLoc......
  • python参数,既有单引号又有双引号的解决办法
       python参数,既有单引号又有双引号的解决办法:使用双引号引起来,中间的双引号使用\转义,中间的单引号不转义,如下python3./pluginTool.pyout/228out/tr069Transform_ass.xmltr069Transform_assmodify/tr069Transform_ass:Device/tr069Transform_ass:Services/tr069Tran......
  • python批量处理Excel数据
    #1、批量提取一个工作簿中所有工作表的特定数据'''importxlwingsasxwimportpandasaspdapp=xw.App(visible=False,add_book=False)workbook=app.books.open('采购.xlsx')#打开工作簿worksheet=workbook.sheets#列出工作簿中的所有工作表data=[]#创......