Có thể lập trình song song trong python không?

Tìm hiểu cách sử dụng các hàm, luồng và khả năng đa xử lý không đồng bộ của Python để sắp xếp các tác vụ và cải thiện khả năng phản hồi của các ứng dụng của bạn

Bởi Serdar Yegulalp

Nhà văn cao cấp, InfoWorld.

graeme nicholson / Getty Images

Mục lục

Cho xem nhiều hơn

Nếu bạn lập trình bằng Python, rất có thể bạn đã gặp phải các tình huống muốn tăng tốc một số thao tác bằng cách thực hiện nhiều tác vụ song song hoặc bằng cách xen kẽ giữa nhiều tác vụ

Python có các cơ chế để thực hiện cả hai cách tiếp cận này, mà chúng tôi gọi là song song và đồng thời. Trong bài viết này, chúng tôi sẽ trình bày chi tiết sự khác biệt giữa song song và đồng thời, đồng thời thảo luận về cách Python có thể sử dụng các kỹ thuật này ở nơi hợp lý nhất

[ Theo dõi các video hướng dẫn về Python thông minh của Serdar Yegulalp để tìm hiểu các thủ thuật Python thông minh trong 5 phút hoặc ít hơn ]

Đồng thời vs. sự song song

Đồng thời và song song là tên của hai cơ chế khác nhau để tung hứng các tác vụ trong lập trình. Đồng thời liên quan đến việc cho phép nhiều công việc thay phiên nhau truy cập vào cùng một tài nguyên được chia sẻ, như đĩa, mạng hoặc một lõi CPU. Tính song song là về việc cho phép một số tác vụ chạy song song trên các tài nguyên được phân vùng độc lập, chẳng hạn như nhiều lõi CPU

Đồng thời và song song có các mục tiêu khác nhau. Mục tiêu của đồng thời là ngăn chặn các tác vụ chặn lẫn nhau bằng cách chuyển đổi giữa chúng khi một tác vụ buộc phải đợi trên một tài nguyên bên ngoài. Một ví dụ phổ biến là hoàn thành nhiều yêu cầu mạng. Cách thô sơ để làm điều đó là khởi chạy một yêu cầu, đợi nó hoàn thành, khởi chạy một yêu cầu khác, v.v. Cách thực hiện đồng thời là khởi chạy tất cả các yêu cầu cùng một lúc, sau đó chuyển đổi giữa chúng khi chúng nhận được phản hồi. Thông qua đồng thời, chúng tôi có thể tổng hợp tất cả thời gian chờ phản hồi

Ngược lại, tính song song là tối đa hóa việc sử dụng tài nguyên phần cứng. Nếu bạn có tám lõi CPU, bạn không muốn chỉ sử dụng tối đa một lõi trong khi bảy lõi còn lại không hoạt động. Thay vào đó, bạn muốn khởi chạy các quy trình hoặc luồng sử dụng tất cả các lõi đó, nếu có thể

Cách Python thực hiện đồng thời và song song

Python cung cấp các cơ chế cho cả đồng thời và song song, mỗi cơ chế có cú pháp và trường hợp sử dụng riêng

Python có hai cơ chế khác nhau để triển khai đồng thời, mặc dù chúng chia sẻ nhiều thành phần chung. Đây là luồng và coroutines hoặc không đồng bộ

Đối với tính song song, Python cung cấp đa xử lý, khởi chạy nhiều phiên bản của trình thông dịch Python, mỗi phiên bản chạy độc lập trên luồng phần cứng của chính nó

Cả ba cơ chế này - phân luồng, coroutines và đa xử lý - đều có các trường hợp sử dụng khác nhau rõ ràng. Threading và coroutines thường có thể được sử dụng thay thế cho nhau, nhưng không phải lúc nào cũng vậy. Đa xử lý là mạnh nhất, được sử dụng cho các tình huống mà bạn cần tối đa hóa việc sử dụng CPU

luồng Python

Nếu bạn đã quen với threading nói chung, thread trong Python sẽ không phải là một bước tiến lớn. Các luồng trong Python là các đơn vị công việc mà bạn có thể nhận một hoặc nhiều hàm và thực thi chúng một cách độc lập với phần còn lại của chương trình. Sau đó, bạn có thể tổng hợp kết quả, thường bằng cách đợi tất cả các luồng chạy đến khi hoàn thành

Một ví dụ đơn giản về phân luồng trong Python

from concurrent.futures import ThreadPoolExecutor
import urllib.request as ur

datas = []

def get_from[url]:
    connection = ur.urlopen[url]
    data = connection.read[]
    datas.append[data]

urls = [
    "//python.org",
    "//docs.python.org/"
    "//wikipedia.org",
    "//imdb.com",    
]

with ThreadPoolExecutor[] as ex:
    for url in urls:
        ex.submit[get_from, url]
       
# let's just look at the beginning of each data stream
# as this could be a lot of data
print [[_[:200] for _ in datas]]

Đoạn mã này sử dụng luồng để đọc dữ liệu từ nhiều URL cùng một lúc, sử dụng nhiều phiên bản được thực thi của hàm

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
6. Các kết quả sau đó được lưu trữ trong một danh sách

Thay vì tạo các luồng trực tiếp, ví dụ này sử dụng một trong các cơ chế thuận tiện của Python để chạy các luồng,

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
7. Chúng tôi có thể gửi hàng chục URL theo cách này mà không làm mọi thứ chậm lại nhiều vì mỗi luồng sẽ nhường cho các luồng khác bất cứ khi nào nó chỉ chờ máy chủ từ xa phản hồi

Người dùng Python thường nhầm lẫn về việc liệu các luồng trong Python có giống với các luồng được hiển thị bởi hệ điều hành bên dưới hay không. Trong CPython, triển khai Python mặc định được sử dụng trong phần lớn các ứng dụng Python, các luồng Python là các luồng hệ điều hành - chúng chỉ được quản lý bởi thời gian chạy Python để chạy hợp tác, mang lại cho nhau khi cần

Ưu điểm của chủ đề Python

Các luồng trong Python cung cấp một cách thuận tiện, dễ hiểu để chạy các tác vụ đang chờ trên các tài nguyên khác. Ví dụ trên có một cuộc gọi mạng, nhưng các tác vụ chờ khác có thể bao gồm tín hiệu từ thiết bị phần cứng hoặc tín hiệu từ luồng chính của chương trình

Ngoài ra, như được hiển thị trong đoạn mã trên, thư viện chuẩn của Python đi kèm với các tiện ích cấp cao để chạy các hoạt động trong luồng. Bạn không cần biết cách các chuỗi hệ điều hành hoạt động để sử dụng các chuỗi Python

Nhược điểm của chủ đề Python

Như đã đề cập trước đây, chủ đề là hợp tác. Thời gian chạy Python phân chia sự chú ý của nó giữa chúng, để các đối tượng được truy cập bởi các luồng có thể được quản lý chính xác. Do đó, các luồng không nên được sử dụng cho công việc sử dụng nhiều CPU. Nếu bạn chạy một thao tác sử dụng nhiều CPU trong một luồng, thao tác đó sẽ bị tạm dừng khi thời gian chạy chuyển sang một luồng khác, do đó sẽ không có lợi về hiệu suất khi chạy thao tác đó bên ngoài luồng

Một nhược điểm khác của luồng là bạn, lập trình viên, chịu trách nhiệm quản lý trạng thái giữa chúng. Trong ví dụ trên, trạng thái duy nhất bên ngoài luồng là nội dung của danh sách

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
8, danh sách này chỉ tổng hợp kết quả từ mỗi luồng. Đồng bộ hóa duy nhất cần thiết được cung cấp tự động bởi thời gian chạy Python khi chúng tôi thêm vào danh sách. Chúng tôi cũng không kiểm tra trạng thái của đối tượng đó cho đến khi tất cả các luồng chạy hoàn thành

Tuy nhiên, nếu chúng tôi đọc và ghi vào

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
8 từ các chuỗi khác nhau, chúng tôi cần phải đồng bộ hóa các quy trình này theo cách thủ công để đảm bảo chúng tôi nhận được kết quả như mong đợi. Mô-đun
import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
0 có các công cụ để thực hiện điều này, nhưng nhà phát triển phải sử dụng chúng — và chúng đủ phức tạp để xứng đáng có một bài viết riêng

Các coroutine của Python và
import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
1

Coroutines hoặc

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
1 là một cách khác để thực thi các chức năng đồng thời trong Python, thông qua các cấu trúc lập trình đặc biệt thay vì các luồng hệ thống. Các coroutine cũng được quản lý bởi thời gian chạy Python nhưng yêu cầu ít chi phí hơn nhiều so với các luồng

Đây là một phiên bản khác của chương trình trước đó, được viết dưới dạng cấu trúc async/coroutine và sử dụng thư viện hỗ trợ xử lý không đồng bộ các yêu cầu mạng

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
6 là một coroutine, tôi. e. một đối tượng chức năng có thể chạy song song với các coroutine khác.
import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
1 khởi chạy một số coroutine [nhiều phiên bản của
import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
6 tìm nạp các URL khác nhau], đợi cho đến khi tất cả chúng chạy hoàn tất, sau đó trả về kết quả tổng hợp của chúng dưới dạng danh sách

Thư viện

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
3 cho phép thực hiện kết nối mạng không đồng bộ. Chúng tôi không thể sử dụng
import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
4 cũ đơn giản trong một coroutine, vì nó sẽ chặn tiến trình của các yêu cầu không đồng bộ khác

Ưu điểm của coroutine Python

Các coroutine hoàn toàn rõ ràng trong cú pháp của chương trình mà các chức năng chạy song song với nhau. Nhìn thoáng qua bạn có thể biết rằng

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
6 là một coroutine. Với các luồng, bất kỳ chức năng nào cũng có thể được chạy trong một luồng, khiến việc suy luận về những gì có thể đang chạy trong một luồng trở nên khó khăn hơn

Một ưu điểm khác của coroutines là chúng không bị ràng buộc bởi một số hạn chế về kiến ​​trúc khi sử dụng các luồng. Nếu bạn có nhiều coroutine, sẽ có ít chi phí hơn liên quan đến việc chuyển đổi giữa chúng và các coroutine yêu cầu bộ nhớ ít hơn một chút so với luồng. Các coroutine thậm chí không yêu cầu các luồng, vì chúng có thể được quản lý trực tiếp bởi thời gian chạy Python, mặc dù chúng có thể được chạy trong các luồng riêng biệt nếu cần

Nhược điểm của Python coroutine

Coroutines và

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
1 yêu cầu viết mã tuân theo cú pháp riêng biệt của nó, việc sử dụng
import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
7 và
import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
8. Mã như vậy, theo thiết kế, không thể trộn lẫn với mã đồng bộ. Đối với những lập trình viên không quen suy nghĩ về cách mã của họ có thể chạy không đồng bộ, việc sử dụng coroutines và async thể hiện một đường cong học tập

Ngoài ra, coroutines và

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
1 không cho phép các tác vụ sử dụng nhiều CPU chạy song song hiệu quả. Đối với các luồng, chúng được thiết kế cho các hoạt động cần đợi ở một số điều kiện bên ngoài

đa xử lý Python

Đa xử lý cho phép bạn chạy song song nhiều tác vụ sử dụng nhiều CPU bằng cách khởi chạy nhiều bản sao độc lập của thời gian chạy Python. Mỗi phiên bản Python nhận mã và dữ liệu cần thiết để chạy tác vụ được đề cập

Đây là tập lệnh đọc web của chúng tôi được viết lại để sử dụng đa xử lý

import urllib.request as ur
from multiprocessing import Pool
import re

urls = [
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
    "//imdb.com",    
]

meta_match = re.compile[""]

def get_from[url]:
    connection = ur.urlopen[url]
    data = str[connection.read[]]
    return meta_match.findall[data]

def main[]:
    with Pool[] as p:
        datas = p.map[get_from, urls]
    print [datas]
# We're not truncating data here,
# since we're only getting extracts anyway
if __name__ == "__main__": main[]

Đối tượng ________ 70 đại diện cho một nhóm quy trình có thể tái sử dụng.

import urllib.request as ur
from multiprocessing import Pool
import re

urls = [
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
    "//imdb.com",    
]

meta_match = re.compile[""]

def get_from[url]:
    connection = ur.urlopen[url]
    data = str[connection.read[]]
    return meta_match.findall[data]

def main[]:
    with Pool[] as p:
        datas = p.map[get_from, urls]
    print [datas]
# We're not truncating data here,
# since we're only getting extracts anyway
if __name__ == "__main__": main[]
1 cho phép bạn gửi một chức năng để chạy qua các quy trình này và có thể lặp lại để phân phối giữa từng phiên bản của chức năng — trong trường hợp này là
import urllib.request as ur
from multiprocessing import Pool
import re

urls = [
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
    "//imdb.com",    
]

meta_match = re.compile[""]

def get_from[url]:
    connection = ur.urlopen[url]
    data = str[connection.read[]]
    return meta_match.findall[data]

def main[]:
    with Pool[] as p:
        datas = p.map[get_from, urls]
    print [datas]
# We're not truncating data here,
# since we're only getting extracts anyway
if __name__ == "__main__": main[]
2 và danh sách các URL

Một điểm khác biệt quan trọng khác trong phiên bản tập lệnh này là chúng tôi thực hiện thao tác liên kết với CPU trong

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
6. Biểu thức chính quy tìm kiếm bất kỳ thứ gì trông giống như thẻ
import urllib.request as ur
from multiprocessing import Pool
import re

urls = [
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
    "//imdb.com",    
]

meta_match = re.compile[""]

def get_from[url]:
    connection = ur.urlopen[url]
    data = str[connection.read[]]
    return meta_match.findall[data]

def main[]:
    with Pool[] as p:
        datas = p.map[get_from, urls]
    print [datas]
# We're not truncating data here,
# since we're only getting extracts anyway
if __name__ == "__main__": main[]
4. Tất nhiên, đây không phải là cách lý tưởng để tìm kiếm những thứ như vậy, nhưng vấn đề là chúng ta có thể thực hiện những gì có thể là một hoạt động tốn kém về mặt tính toán trong
import urllib.request as ur
from multiprocessing import Pool
import re

urls = [
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
    "//imdb.com",    
]

meta_match = re.compile[""]

def get_from[url]:
    connection = ur.urlopen[url]
    data = str[connection.read[]]
    return meta_match.findall[data]

def main[]:
    with Pool[] as p:
        datas = p.map[get_from, urls]
    print [datas]
# We're not truncating data here,
# since we're only getting extracts anyway
if __name__ == "__main__": main[]
2 mà không bị nó chặn tất cả các yêu cầu khác

Ưu điểm của đa xử lý Python

Với luồng và coroutines, thời gian chạy Python buộc tất cả các hoạt động phải chạy tuần tự, càng tốt để quản lý quyền truy cập vào bất kỳ đối tượng Python nào. Đa xử lý vượt qua giới hạn này bằng cách cung cấp cho mỗi thao tác một thời gian chạy Python riêng biệt và một lõi CPU đầy đủ

Nhược điểm của đa xử lý Python

Đa xử lý có hai nhược điểm khác biệt. Đầu tiên, có thêm chi phí liên quan đến việc tạo các quy trình. Tuy nhiên, bạn có thể giảm thiểu tác động của việc này nếu bạn quay vòng các quy trình đó một lần trong suốt thời gian tồn tại của ứng dụng và sử dụng lại chúng. Đối tượng

import urllib.request as ur
from multiprocessing import Pool
import re

urls = [
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
    "//imdb.com",    
]

meta_match = re.compile[""]

def get_from[url]:
    connection = ur.urlopen[url]
    data = str[connection.read[]]
    return meta_match.findall[data]

def main[]:
    with Pool[] as p:
        datas = p.map[get_from, urls]
    print [datas]
# We're not truncating data here,
# since we're only getting extracts anyway
if __name__ == "__main__": main[]
6 mà chúng ta sử dụng trong ví dụ trên có thể hoạt động như thế này. Sau khi được thiết lập, chúng tôi có thể gửi công việc tới nó khi cần, do đó, chỉ có chi phí một lần trong suốt vòng đời của chương trình để bắt đầu các quy trình con

Nhược điểm thứ hai là mỗi quy trình con cần có một bản sao dữ liệu mà nó hoạt động được gửi tới nó từ quy trình chính. Nói chung, mỗi quy trình con cũng phải trả lại dữ liệu cho quy trình chính. Để làm điều này, nó sử dụng giao thức

import urllib.request as ur
from multiprocessing import Pool
import re

urls = [
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
    "//imdb.com",    
]

meta_match = re.compile[""]

def get_from[url]:
    connection = ur.urlopen[url]
    data = str[connection.read[]]
    return meta_match.findall[data]

def main[]:
    with Pool[] as p:
        datas = p.map[get_from, urls]
    print [datas]
# We're not truncating data here,
# since we're only getting extracts anyway
if __name__ == "__main__": main[]
7 của Python, giao thức này tuần tự hóa các đối tượng Python thành dạng nhị phân. Các đối tượng phổ biến [số, chuỗi, danh sách, từ điển, bộ dữ liệu, byte, v.v. ] đều được hỗ trợ nhưng một số loại đối tượng kỳ lạ có thể không hoạt động

Sử dụng hình thức đồng thời Python nào

Bất cứ khi nào bạn đang thực hiện các hoạt động sử dụng nhiều CPU trong thời gian dài, hãy sử dụng tính năng đa xử lý. “Sử dụng nhiều CPU” có thể liên quan đến cả công việc xảy ra trực tiếp trong thời gian chạy Python [e. g. , ví dụ về biểu thức chính quy ở trên] và hoàn thành công việc với thư viện bên ngoài như NumPy. Trong cả hai trường hợp, bạn không muốn thời gian chạy Python bị hạn chế trong một phiên bản duy nhất chặn khi thực hiện công việc dựa trên CPU

[ Cập nhật những phát triển mới nhất về Python và phát triển phần mềm. Đăng ký nhận bản tin Cái nhìn đầu tiên của InfoWorld ]

Đối với các hoạt động không liên quan đến CPU nhưng yêu cầu chờ tài nguyên bên ngoài, chẳng hạn như cuộc gọi mạng, hãy sử dụng phân luồng hoặc coroutines. Mặc dù sự khác biệt về hiệu quả giữa hai loại này là không đáng kể khi chỉ xử lý một số tác vụ cùng một lúc, nhưng các coroutine sẽ hiệu quả hơn khi xử lý hàng nghìn tác vụ, vì bộ thực thi sẽ dễ dàng quản lý số lượng lớn các coroutine hơn là số lượng lớn các luồng.

Cuối cùng, lưu ý rằng các coroutine hoạt động tốt nhất khi sử dụng các thư viện thân thiện với không đồng bộ, chẳng hạn như

import aiohttp
import asyncio

urls = [
    "//imdb.com",    
    "//python.org",
    "//docs.python.org",
    "//wikipedia.org",
]

async def get_from[session, url]:
    async with session.get[url] as r:
        return await r.text[]


async def main[]:
    async with aiohttp.ClientSession[] as session:
        datas = await asyncio.gather[*[get_from[session, u] for u in urls]]
        print [[_[:200] for _ in datas]]

if __name__ == "__main__":
    loop = asyncio.get_event_loop[]
    loop.run_until_complete[main[]]
3 trong ví dụ trên. Nếu các coroutine của bạn không thân thiện với async, chúng có thể cản trở tiến trình của các coroutine khác

Có liên quan

  • con trăn
  • Ngôn ngữ lập trình
  • Phát triển phần mềm
  • đồng thời

Serdar Yegulalp là một nhà văn cao cấp tại InfoWorld, tập trung vào học máy, container hóa, devops, hệ sinh thái Python và đánh giá định kỳ

Python có tốt cho lập trình song song không?

Python cung cấp cơ chế cho cả đồng thời và song song , mỗi cơ chế có cú pháp và trường hợp sử dụng riêng. Python có hai cơ chế khác nhau để triển khai đồng thời, mặc dù chúng chia sẻ nhiều thành phần chung. Đây là luồng và coroutines hoặc không đồng bộ.

Python có tính toán song song không?

Song song trong Python [và các ngôn ngữ lập trình khác] cho phép nhà phát triển chạy đồng thời nhiều phần của chương trình . Hầu hết các PC hiện đại, máy trạm và thậm chí cả thiết bị di động đều có nhiều lõi bộ xử lý trung tâm [CPU].

Python có thể chạy song song hai chức năng không?

Một cách phổ biến để chạy các chức năng song song với Python là sử dụng mô-đun đa xử lý . Mô-đun này mạnh mẽ, có nhiều tùy chọn để định cấu hình và nhiều thứ để điều chỉnh.

Chủ Đề