Python中并发方面的差异

人们可能会想象Python才刚刚引入这些概念或能力,因为随着Python 3的发布,我们听到了很多关于异步操作和并发的新趋势。

许多新手可能认为使用asyncio 是执行并发和异步活动的唯一实用方法。本文将讨论我们如何在Python中实现并发及其好处或坏处。

线程和多线程

线程在Python中已经存在很长时间了。因此,由于线程的存在,我们可以同时执行多个操作。

不幸的是,CPython ,一个典型的Python主线版本,仍然使用全局解释器锁 (GIL),这使得多线程应用–现今常见的实现并行处理的方法–不是很理想。

Python引入了GIL ,以使CPython的内存处理更易于管理与C的集成(例如,扩展)。

GIL 是一种锁定机制,即Python解释器只能同时运行一个线程。Python的byte 代码永远只能由一个线程同时执行。

示例代码:

import threading
import time
import random
def worker(num):
    sec = random.randrange(1, 5)
    time.sleep(sec)
    print("I am thread {}, who slept for {} seconds.".format(num, sec))
for i in range(3):
    t = threading.Thread(target=worker, args=(i,))
    t.start()
print("Completed!")

输出:

Completed!
I am thread 1, who slept for 3 seconds.
I am thread 3, who slept for 2 seconds.
I am thread 4, who slept for 4 seconds.

进程和多进程

多进程利用了许多CPU。我们可以有效地同时进行几个任务,因为每个CPU都是并行运行的。对于那些受CPU约束的工作,多处理是你想使用的。

Python引入了multiprocessing 模块来实现并行化,如果你使用过线程,会感觉非常相似。

示例代码:

import multiprocessing
import time
import random
def worker(num):
    sec = random.randrange(1, 5)
    time.sleep(sec)
    print("I am process {}, who slept for {} seconds.".format(num, sec))
for i in range(3):
    t = multiprocessing.Process(target=worker, args=(i,))
    t.start()
print("Completed")

输出:

Completed
I am process 1, who slept for 1 seconds.
I am process 2, who slept for 2 seconds.
I am process 0, who slept for 3 seconds.

与其说是多线程,不如说是在你的CPU的不同核心上运行多个进程,这使我们的Python脚本更快。

异步和asyncio

在同步操作中,任务是同步进行的,一个接一个。然而,在异步操作中,任务的开始可能完全是相互独立的。

一个async 任务可能开始并继续运行,而执行则切换到另一个活动。另一方面,异步任务经常在后台执行,不会阻塞(让执行者等待完成)。

除了其他有价值的功能外,asyncio 还提供了一个事件循环。事件循环监控各种I/O事件,切换到准备好的任务,并暂停等待I/O的任务。

因此,我们不会将时间浪费在未完成的项目上。

示例代码:

#Python小白学习交流群:711312441
import asyncio
import datetime
import random
async def my_sleep_func():
    await asyncio.sleep(random.randint(0, 5))
async def displayDate(num, loop):
    endTime = loop.time() + 60.0
    while True:
        print("Loop: {} Time: {}".format(num, datetime.datetime.now()))
        if (loop.time() + 1.0) >= endTime:
            break
        await my_sleep_func()
loop = asyncio.get_event_loop()
asyncio.ensure_future(displayDate(1, loop))
asyncio.ensure_future(displayDate(2, loop))
loop.run_forever()

如果我们走过上面的代码片断:

  • 我们有一个async 函数displayDate ,它接受一个数字和事件循环作为参数。
  • 上述函数有一个无限循环,60秒后停止。但在这60秒内,它重复地打印出时间并打盹。
  • await 函数可以等待其他async 函数完成。
  • 我们将该函数传递给事件循环(使用ensure_future 函数)。
  • 我们开始运行事件循环。

每当调用await ,asyncio 明白该函数可能需要一些时间。当asyncio 注意到停止的函数的I/O已经准备好了,它就恢复进程。

现在,问题是,在这三种形式的并发中,我们需要使用什么?我们可以注意到以下几点来帮助我们决策:

  • 对CPU绑定的操作使用多处理。
  • 对I/O Bound、快速I/O和有限的连接数使用多线程。
  • 对于I/O绑定、慢速I/O和许多连接,使用异步IO。
  • asyncio/await 在Python 3.5和更高版本上工作。

我们也可以参考下面的伪代码:

if io_bound:
    if io_very_slow:
        print("Use asyncio")
    else:
        print("Use multithreading")
else:
    print("multiprocessing")

猜你喜欢

转载自blog.csdn.net/qdPython/article/details/132045413