Python có tốt cho luồng không?

Phân luồng chỉ là một trong nhiều cách có thể xây dựng các chương trình đồng thời. Trong bài viết này, chúng ta sẽ xem xét phân luồng và một vài chiến lược khác để xây dựng các chương trình đồng thời trong Python, cũng như thảo luận về cách mỗi chiến lược phù hợp trong các tình huống khác nhau

Qua

Marcus McCurdy

Marcus là một lập trình viên tài năng và xuất sắc trong lĩnh vực phát triển back-end. Tuy nhiên, anh cảm thấy thoải mái với tư cách là một full stack developer

CHIA SẺ

CHIA SẺ

Ghi chú. Theo yêu cầu phổ biến, tôi trình bày một số kỹ thuật thay thế---bao gồm cả async/await, chỉ khả dụng kể từ khi Python 3 ra đời. 5---Tôi đã thêm. Vui thích

Các cuộc thảo luận chỉ trích Python thường nói về việc khó sử dụng Python cho công việc đa luồng như thế nào, chỉ tay vào cái được gọi là khóa trình thông dịch toàn cầu [được gọi một cách trìu mến là GIL] ngăn nhiều luồng mã Python chạy đồng thời. Do đó, mô-đun đa luồng Python không hoạt động hoàn toàn theo cách bạn mong đợi nếu bạn không phải là nhà phát triển Python và bạn đến từ các ngôn ngữ khác như C++ hoặc Java. Cần phải làm rõ rằng người ta vẫn có thể viết mã bằng Python chạy đồng thời hoặc song song và tạo ra sự khác biệt rõ rệt về hiệu suất kết quả, miễn là một số điều nhất định được xem xét. Nếu bạn chưa đọc nó, tôi khuyên bạn nên xem bài viết của Eqbal Qur'an về đồng thời và song song trong Ruby tại đây trên Toptal Engineering Blog

Để chứng minh tính đồng thời trong Python, chúng tôi sẽ viết một tập lệnh nhỏ để tải xuống những hình ảnh phổ biến hàng đầu từ Imgur. Chúng tôi sẽ bắt đầu với phiên bản tải xuống hình ảnh tuần tự hoặc từng hình ảnh một. Điều kiện tiên quyết, bạn sẽ phải đăng ký một ứng dụng trên Imgur. Nếu bạn chưa có tài khoản Imgur, vui lòng tạo một tài khoản trước

Các tập lệnh trong các ví dụ đa luồng Python này đã được thử nghiệm với Python 3. 6. 4. Với một số thay đổi, chúng cũng nên chạy với Python 2—urllib là thứ đã thay đổi nhiều nhất giữa hai phiên bản Python này

Bắt đầu với đa luồng Python

Hãy để chúng tôi bắt đầu bằng cách tạo một mô-đun Python, có tên là download.py. Tệp này sẽ chứa tất cả các chức năng cần thiết để tìm nạp danh sách hình ảnh và tải chúng xuống. Chúng tôi sẽ chia các chức năng này thành ba chức năng riêng biệt

  • import logging
    import os
    from time import time
    
    from download import setup_download_dir, get_links, download_link
    
    logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
    logger = logging.getLogger[__name__]
    
    def main[]:
        ts = time[]
        client_id = os.getenv['IMGUR_CLIENT_ID']
        if not client_id:
            raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
        download_dir = setup_download_dir[]
        links = get_links[client_id]
        for link in links:
            download_link[download_dir, link]
        logging.info['Took %s seconds', time[] - ts]
    
    if __name__ == '__main__':
        main[]
    
    0
  • import logging
    import os
    from time import time
    
    from download import setup_download_dir, get_links, download_link
    
    logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
    logger = logging.getLogger[__name__]
    
    def main[]:
        ts = time[]
        client_id = os.getenv['IMGUR_CLIENT_ID']
        if not client_id:
            raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
        download_dir = setup_download_dir[]
        links = get_links[client_id]
        for link in links:
            download_link[download_dir, link]
        logging.info['Took %s seconds', time[] - ts]
    
    if __name__ == '__main__':
        main[]
    
    1
  • import logging
    import os
    from time import time
    
    from download import setup_download_dir, get_links, download_link
    
    logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
    logger = logging.getLogger[__name__]
    
    def main[]:
        ts = time[]
        client_id = os.getenv['IMGUR_CLIENT_ID']
        if not client_id:
            raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
        download_dir = setup_download_dir[]
        links = get_links[client_id]
        for link in links:
            download_link[download_dir, link]
        logging.info['Took %s seconds', time[] - ts]
    
    if __name__ == '__main__':
        main[]
    
    2

Chức năng thứ ba,

import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
2, sẽ được sử dụng để tạo thư mục đích tải xuống nếu nó chưa tồn tại

API của Imgur yêu cầu các yêu cầu HTTP mang tiêu đề

import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
4 với ID ứng dụng khách. Bạn có thể tìm ID khách hàng này từ bảng điều khiển của ứng dụng mà bạn đã đăng ký trên Imgur và phản hồi sẽ được mã hóa JSON. Chúng ta có thể sử dụng thư viện JSON chuẩn của Python để giải mã nó. Tải xuống hình ảnh thậm chí còn đơn giản hơn, vì tất cả những gì bạn phải làm là tìm nạp hình ảnh theo URL của nó và ghi nó vào một tệp

Đây là những gì kịch bản trông giống như

import json
import logging
import os
from pathlib import Path
from urllib.request import urlopen, Request

logger = logging.getLogger[__name__]

types = {'image/jpeg', 'image/png'}


def get_links[client_id]:
    headers = {'Authorization': 'Client-ID {}'.format[client_id]}
    req = Request['//api.imgur.com/3/gallery/random/random/', headers=headers, method='GET']
    with urlopen[req] as resp:
        data = json.loads[resp.read[].decode['utf-8']]
    return [item['link'] for item in data['data'] if 'type' in item and item['type'] in types]


def download_link[directory, link]:
    download_path = directory / os.path.basename[link]
    with urlopen[link] as image, download_path.open['wb'] as f:
        f.write[image.read[]]
    logger.info['Downloaded %s', link]


def setup_download_dir[]:
    download_dir = Path['images']
    if not download_dir.exists[]:
        download_dir.mkdir[]
    return download_dir

Tiếp theo, chúng ta sẽ cần viết một mô-đun sẽ sử dụng các chức năng này để tải xuống từng hình ảnh một. Chúng tôi sẽ đặt tên này là

import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
5. Điều này sẽ chứa chức năng chính của phiên bản ngây thơ đầu tiên của trình tải xuống hình ảnh Imgur. Mô-đun sẽ truy xuất ID ứng dụng khách Imgur trong biến môi trường
import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
6. Nó sẽ gọi
import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
2 để tạo thư mục đích tải xuống. Cuối cùng, nó sẽ lấy một danh sách các hình ảnh bằng cách sử dụng chức năng
import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
0, lọc ra tất cả các URL của GIF và album, sau đó sử dụng
import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
1 để tải xuống và lưu từng hình ảnh đó vào đĩa. Đây là những gì
import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
5 trông giống như

import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]

Trên máy tính xách tay của tôi, tập lệnh này mất 19. 4 giây để tải xuống 91 hình ảnh. Xin lưu ý rằng những con số này có thể thay đổi tùy theo mạng bạn đang sử dụng. 19. 4 giây không quá dài, nhưng nếu chúng ta muốn tải thêm ảnh thì sao? . Với trung bình là 0. 2 giây cho mỗi ảnh, 900 ảnh sẽ mất khoảng 3 phút. Đối với 9000 bức ảnh, sẽ mất 30 phút. Tin tốt là bằng cách giới thiệu đồng thời hoặc song song, chúng ta có thể tăng tốc độ này lên đáng kể

Tất cả các ví dụ về mã tiếp theo sẽ chỉ hiển thị các câu lệnh nhập mới và dành riêng cho các ví dụ đó. Để thuận tiện, bạn có thể tìm thấy tất cả các tập lệnh Python này trong kho lưu trữ GitHub này

Tính song song và đồng thời trong Python. Ví dụ đa luồng

Luồng là một trong những cách tiếp cận nổi tiếng nhất để đạt được tính song song và đồng thời trong Python. Luồng là một tính năng thường được cung cấp bởi hệ điều hành. Các luồng nhẹ hơn các quy trình và chia sẻ cùng một không gian bộ nhớ

Trong ví dụ về đa luồng Python này, chúng tôi sẽ viết một mô-đun mới để thay thế

import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
5. Mô-đun này sẽ tạo một nhóm gồm tám luồng, tạo thành tổng cộng chín luồng bao gồm cả luồng chính. Tôi đã chọn tám luồng công nhân vì máy tính của tôi có tám lõi CPU và một luồng công nhân trên mỗi lõi có vẻ là một con số tốt cho số lượng luồng chạy cùng một lúc. Trên thực tế, con số này được chọn cẩn thận hơn nhiều dựa trên các yếu tố khác, chẳng hạn như các ứng dụng và dịch vụ khác đang chạy trên cùng một máy

Điều này gần giống như lớp trước, ngoại trừ việc bây giờ chúng ta có một lớp mới,

import logging
import os
from queue import Queue
from threading import Thread
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


class DownloadWorker[Thread]:

    def __init__[self, queue]:
        Thread.__init__[self]
        self.queue = queue

    def run[self]:
        while True:
            # Get the work from the queue and expand the tuple
            directory, link = self.queue.get[]
            try:
                download_link[directory, link]
            finally:
                self.queue.task_done[]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    # Create a queue to communicate with the worker threads
    queue = Queue[]
    # Create 8 worker threads
    for x in range[8]:
        worker = DownloadWorker[queue]
        # Setting daemon to True will let the main thread exit even though the workers are blocking
        worker.daemon = True
        worker.start[]
    # Put the tasks into the queue as a tuple
    for link in links:
        logger.info['Queueing {}'.format[link]]
        queue.put[[download_dir, link]]
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join[]
    logging.info['Took %s', time[] - ts]

if __name__ == '__main__':
    main[]
2, là hậu duệ của lớp Python
import logging
import os
from queue import Queue
from threading import Thread
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


class DownloadWorker[Thread]:

    def __init__[self, queue]:
        Thread.__init__[self]
        self.queue = queue

    def run[self]:
        while True:
            # Get the work from the queue and expand the tuple
            directory, link = self.queue.get[]
            try:
                download_link[directory, link]
            finally:
                self.queue.task_done[]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    # Create a queue to communicate with the worker threads
    queue = Queue[]
    # Create 8 worker threads
    for x in range[8]:
        worker = DownloadWorker[queue]
        # Setting daemon to True will let the main thread exit even though the workers are blocking
        worker.daemon = True
        worker.start[]
    # Put the tasks into the queue as a tuple
    for link in links:
        logger.info['Queueing {}'.format[link]]
        queue.put[[download_dir, link]]
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join[]
    logging.info['Took %s', time[] - ts]

if __name__ == '__main__':
    main[]
3. Phương thức chạy đã bị ghi đè, chạy một vòng lặp vô hạn. Trên mỗi lần lặp lại, nó gọi
import logging
import os
from queue import Queue
from threading import Thread
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


class DownloadWorker[Thread]:

    def __init__[self, queue]:
        Thread.__init__[self]
        self.queue = queue

    def run[self]:
        while True:
            # Get the work from the queue and expand the tuple
            directory, link = self.queue.get[]
            try:
                download_link[directory, link]
            finally:
                self.queue.task_done[]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    # Create a queue to communicate with the worker threads
    queue = Queue[]
    # Create 8 worker threads
    for x in range[8]:
        worker = DownloadWorker[queue]
        # Setting daemon to True will let the main thread exit even though the workers are blocking
        worker.daemon = True
        worker.start[]
    # Put the tasks into the queue as a tuple
    for link in links:
        logger.info['Queueing {}'.format[link]]
        queue.put[[download_dir, link]]
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join[]
    logging.info['Took %s', time[] - ts]

if __name__ == '__main__':
    main[]
4 để thử và tìm nạp một URL từ hàng đợi an toàn cho luồng. Nó chặn cho đến khi có một mục trong hàng đợi để nhân viên xử lý. Khi worker nhận được một mục từ hàng đợi, nó sẽ gọi phương thức
import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
1 tương tự đã được sử dụng trong tập lệnh trước đó để tải hình ảnh xuống thư mục hình ảnh. Sau khi tải xuống xong, nhân viên báo hiệu cho hàng đợi rằng nhiệm vụ đó đã hoàn thành. Điều này rất quan trọng, bởi vì Hàng đợi theo dõi có bao nhiêu tác vụ được xử lý. Cuộc gọi đến
import logging
import os
from queue import Queue
from threading import Thread
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


class DownloadWorker[Thread]:

    def __init__[self, queue]:
        Thread.__init__[self]
        self.queue = queue

    def run[self]:
        while True:
            # Get the work from the queue and expand the tuple
            directory, link = self.queue.get[]
            try:
                download_link[directory, link]
            finally:
                self.queue.task_done[]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    # Create a queue to communicate with the worker threads
    queue = Queue[]
    # Create 8 worker threads
    for x in range[8]:
        worker = DownloadWorker[queue]
        # Setting daemon to True will let the main thread exit even though the workers are blocking
        worker.daemon = True
        worker.start[]
    # Put the tasks into the queue as a tuple
    for link in links:
        logger.info['Queueing {}'.format[link]]
        queue.put[[download_dir, link]]
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join[]
    logging.info['Took %s', time[] - ts]

if __name__ == '__main__':
    main[]
6 sẽ chặn luồng chính mãi mãi nếu các công nhân không báo hiệu rằng họ đã hoàn thành một nhiệm vụ

import logging
import os
from queue import Queue
from threading import Thread
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


class DownloadWorker[Thread]:

    def __init__[self, queue]:
        Thread.__init__[self]
        self.queue = queue

    def run[self]:
        while True:
            # Get the work from the queue and expand the tuple
            directory, link = self.queue.get[]
            try:
                download_link[directory, link]
            finally:
                self.queue.task_done[]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    # Create a queue to communicate with the worker threads
    queue = Queue[]
    # Create 8 worker threads
    for x in range[8]:
        worker = DownloadWorker[queue]
        # Setting daemon to True will let the main thread exit even though the workers are blocking
        worker.daemon = True
        worker.start[]
    # Put the tasks into the queue as a tuple
    for link in links:
        logger.info['Queueing {}'.format[link]]
        queue.put[[download_dir, link]]
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join[]
    logging.info['Took %s', time[] - ts]

if __name__ == '__main__':
    main[]

Chạy tập lệnh ví dụ đa luồng Python này trên cùng một máy được sử dụng trước đó dẫn đến thời gian tải xuống là 4. 1 giây. Đó là 4. Nhanh hơn 7 lần so với ví dụ trước. Mặc dù điều này nhanh hơn nhiều, nhưng điều đáng nói là chỉ có một luồng được thực thi tại một thời điểm trong suốt quá trình này do GIL. Do đó, mã này đồng thời nhưng không song song. Lý do nó vẫn nhanh hơn là vì đây là tác vụ bị ràng buộc IO. Bộ xử lý hầu như không đổ mồ hôi khi tải xuống những hình ảnh này và phần lớn thời gian dành cho việc chờ mạng. Đây là lý do tại sao đa luồng Python có thể giúp tăng tốc độ lớn. Bộ xử lý có thể chuyển đổi giữa các luồng bất cứ khi nào một trong số chúng sẵn sàng thực hiện một số công việc. Sử dụng mô-đun luồng trong Python hoặc bất kỳ ngôn ngữ được giải thích nào khác với GIL thực sự có thể dẫn đến giảm hiệu suất. Nếu mã của bạn đang thực hiện một tác vụ bị ràng buộc bởi CPU, chẳng hạn như giải nén các tệp gzip, việc sử dụng mô-đun

import logging
import os
from queue import Queue
from threading import Thread
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


class DownloadWorker[Thread]:

    def __init__[self, queue]:
        Thread.__init__[self]
        self.queue = queue

    def run[self]:
        while True:
            # Get the work from the queue and expand the tuple
            directory, link = self.queue.get[]
            try:
                download_link[directory, link]
            finally:
                self.queue.task_done[]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    # Create a queue to communicate with the worker threads
    queue = Queue[]
    # Create 8 worker threads
    for x in range[8]:
        worker = DownloadWorker[queue]
        # Setting daemon to True will let the main thread exit even though the workers are blocking
        worker.daemon = True
        worker.start[]
    # Put the tasks into the queue as a tuple
    for link in links:
        logger.info['Queueing {}'.format[link]]
        queue.put[[download_dir, link]]
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join[]
    logging.info['Took %s', time[] - ts]

if __name__ == '__main__':
    main[]
7 sẽ dẫn đến thời gian thực thi chậm hơn. Đối với các tác vụ ràng buộc CPU và thực thi song song thực sự, chúng ta có thể sử dụng mô-đun đa xử lý

Mặc dù triển khai Python tham chiếu thực tế—CPython–có GIL, nhưng điều này không đúng với tất cả các triển khai Python. Ví dụ: IronPython, một triển khai Python sử dụng. NET framework, không có GIL và Jython, triển khai dựa trên Java cũng vậy. Bạn có thể tìm thấy danh sách triển khai Python đang hoạt động

Có liên quan. Mẹo và phương pháp hay nhất về Python của Toptal Developers

Đa xử lý Python. Sinh sản nhiều quá trình

Mô-đun đa xử lý Python dễ thả vào hơn mô-đun luồng, vì chúng ta không cần thêm một lớp như ví dụ đa luồng Python. Những thay đổi duy nhất chúng ta cần thực hiện là trong chức năng chính

Để sử dụng nhiều quy trình, chúng tôi tạo một

import logging
import os
from queue import Queue
from threading import Thread
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


class DownloadWorker[Thread]:

    def __init__[self, queue]:
        Thread.__init__[self]
        self.queue = queue

    def run[self]:
        while True:
            # Get the work from the queue and expand the tuple
            directory, link = self.queue.get[]
            try:
                download_link[directory, link]
            finally:
                self.queue.task_done[]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    # Create a queue to communicate with the worker threads
    queue = Queue[]
    # Create 8 worker threads
    for x in range[8]:
        worker = DownloadWorker[queue]
        # Setting daemon to True will let the main thread exit even though the workers are blocking
        worker.daemon = True
        worker.start[]
    # Put the tasks into the queue as a tuple
    for link in links:
        logger.info['Queueing {}'.format[link]]
        queue.put[[download_dir, link]]
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join[]
    logging.info['Took %s', time[] - ts]

if __name__ == '__main__':
    main[]
8 đa xử lý. Với phương thức bản đồ mà nó cung cấp, chúng tôi sẽ chuyển danh sách URL tới nhóm, từ đó sẽ sinh ra tám quy trình mới và sử dụng từng quy trình để tải xuống hình ảnh song song. Đây là sự song song thực sự, nhưng nó đi kèm với một chi phí. Toàn bộ bộ nhớ của tập lệnh được sao chép vào từng quy trình con được sinh ra. Trong ví dụ đơn giản này, nó không phải là vấn đề lớn, nhưng nó có thể dễ dàng trở thành chi phí nghiêm trọng đối với các chương trình không tầm thường

import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]

Phân phối cho nhiều công nhân

Mặc dù các mô-đun đa luồng và đa xử lý của Python rất phù hợp với các tập lệnh đang chạy trên máy tính cá nhân của bạn, nhưng bạn nên làm gì nếu muốn công việc được thực hiện trên một máy khác hoặc bạn cần mở rộng quy mô hơn CPU trên một máy . Nếu bạn có một số tác vụ chạy dài, bạn không muốn tạo ra một loạt các quy trình phụ hoặc luồng trên cùng một máy cần chạy phần còn lại của mã ứng dụng của bạn. Điều này sẽ làm giảm hiệu suất của ứng dụng cho tất cả người dùng của bạn. Điều tuyệt vời là có thể chạy những công việc này trên một máy khác hoặc nhiều máy khác

Một thư viện Python tuyệt vời cho nhiệm vụ này là RQ, một thư viện rất đơn giản nhưng mạnh mẽ. Trước tiên, bạn liệt kê một hàm và các đối số của nó bằng thư viện. Điều này lựa chọn biểu diễn lệnh gọi hàm, sau đó được thêm vào danh sách Redis. Ghi danh vào công việc là bước đầu tiên, nhưng sẽ không làm gì cả. Chúng tôi cũng cần ít nhất một nhân viên lắng nghe hàng đợi công việc đó

Bước đầu tiên là cài đặt và chạy máy chủ Redis trên máy tính của bạn hoặc có quyền truy cập vào máy chủ Redis đang chạy. Sau đó, chỉ có một vài thay đổi nhỏ được thực hiện đối với mã hiện tại. Trước tiên, chúng tôi tạo một phiên bản của Hàng đợi RQ và chuyển cho nó một phiên bản của máy chủ Redis từ thư viện redis-py. Sau đó, thay vì chỉ gọi phương thức

import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
1 của chúng tôi, chúng tôi gọi
import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
0. Phương thức enqueue lấy một hàm làm đối số đầu tiên của nó, sau đó bất kỳ đối số hoặc đối số từ khóa nào khác được chuyển cùng với hàm đó khi công việc thực sự được thực thi

Một bước cuối cùng chúng ta cần làm là khởi động một số công nhân. RQ cung cấp tập lệnh tiện dụng để chạy công nhân trên hàng đợi mặc định. Chỉ cần chạy

import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
1 trong cửa sổ đầu cuối và nó sẽ bắt đầu một nhân viên lắng nghe trên hàng đợi mặc định. Vui lòng đảm bảo rằng thư mục làm việc hiện tại của bạn giống với nơi lưu trữ tập lệnh. Nếu bạn muốn nghe một hàng đợi khác, bạn có thể chạy
import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
2 và nó sẽ nghe hàng đợi có tên đó. Điều tuyệt vời về RQ là miễn là bạn có thể kết nối với Redis, bạn có thể chạy bao nhiêu công nhân tùy thích trên bao nhiêu máy khác nhau tùy thích; . Đây là nguồn cho phiên bản RQ

import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]

Tuy nhiên, RQ không phải là giải pháp hàng đợi công việc Python duy nhất. RQ rất dễ sử dụng và đáp ứng rất tốt các trường hợp sử dụng đơn giản, nhưng nếu cần các tùy chọn nâng cao hơn, thì có thể sử dụng các giải pháp hàng đợi Python 3 khác [chẳng hạn như Celery]

Đa xử lý so với. Đa luồng trong Python

Nếu mã của bạn bị ràng buộc IO, cả đa xử lý và đa luồng trong Python sẽ phù hợp với bạn. Đa xử lý Python dễ thả vào hơn so với luồng nhưng có chi phí bộ nhớ cao hơn. Nếu mã của bạn bị ràng buộc bởi CPU, đa xử lý rất có thể sẽ là lựa chọn tốt hơn—đặc biệt nếu máy mục tiêu có nhiều lõi hoặc CPU. Đối với các ứng dụng web và khi bạn cần mở rộng quy mô công việc trên nhiều máy, RQ sẽ phù hợp hơn với bạn

Có liên quan. Trở nên cao cấp hơn. Tránh 10 lỗi phổ biến nhất mà các lập trình viên Python mắc phải

 

Cập nhật

Cải thiện tính đồng thời trong Python 3. 2+ Với
import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
3

Một cái gì đó mới kể từ Python 3. 2 không được đề cập đến trong bài viết gốc là gói

import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
3. Gói này cung cấp một cách khác để sử dụng song song và đồng thời với Python

Trong bài viết gốc, tôi đã đề cập rằng mô-đun đa xử lý Python sẽ dễ dàng đưa vào mã hiện có hơn mô-đun phân luồng. Điều này là do mô-đun luồng Python 3 yêu cầu phân lớp con lớp

import logging
import os
from queue import Queue
from threading import Thread
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


class DownloadWorker[Thread]:

    def __init__[self, queue]:
        Thread.__init__[self]
        self.queue = queue

    def run[self]:
        while True:
            # Get the work from the queue and expand the tuple
            directory, link = self.queue.get[]
            try:
                download_link[directory, link]
            finally:
                self.queue.task_done[]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    # Create a queue to communicate with the worker threads
    queue = Queue[]
    # Create 8 worker threads
    for x in range[8]:
        worker = DownloadWorker[queue]
        # Setting daemon to True will let the main thread exit even though the workers are blocking
        worker.daemon = True
        worker.start[]
    # Put the tasks into the queue as a tuple
    for link in links:
        logger.info['Queueing {}'.format[link]]
        queue.put[[download_dir, link]]
    # Causes the main thread to wait for the queue to finish processing all the tasks
    queue.join[]
    logging.info['Took %s', time[] - ts]

if __name__ == '__main__':
    main[]
3 và cũng tạo một
import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
6 để các luồng theo dõi hoạt động

Sử dụng a làm cho mã ví dụ phân luồng Python gần giống với mô-đun đa xử lý

import logging
import os
from concurrent.futures import ThreadPoolExecutor
from functools import partial
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]

    # By placing the executor inside a with block, the executors shutdown method
    # will be called cleaning up threads.
    # 
    # By default, the executor sets number of workers to 5 times the number of
    # CPUs.
    with ThreadPoolExecutor[] as executor:

        # Create a new partially applied function that stores the directory
        # argument.
        # 
        # This allows the download_link function that normally takes two
        # arguments to work with the map function that expects a function of a
        # single argument.
        fn = partial[download_link, download_dir]

        # Executes fn concurrently using threads on the links iterable. The
        # timeout is for the entire process, not a single call, so downloading
        # all images must complete within 30 seconds.
        executor.map[fn, links, timeout=30]


if __name__ == '__main__':
    main[]

Bây giờ chúng ta đã tải xuống tất cả những hình ảnh này bằng Python

import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
7, chúng ta có thể sử dụng chúng để kiểm tra tác vụ liên quan đến CPU. Chúng tôi có thể tạo các phiên bản hình thu nhỏ của tất cả các hình ảnh trong cả tập lệnh đơn luồng, xử lý đơn và sau đó kiểm tra giải pháp dựa trên đa xử lý

Chúng tôi sẽ sử dụng thư viện Gối để xử lý việc thay đổi kích thước của hình ảnh

Đây là kịch bản ban đầu của chúng tôi

import logging
from pathlib import Path
from time import time

from PIL import Image

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


def create_thumbnail[size, path]:
    """
    Creates a thumbnail of an image with the same name as image but with
    _thumbnail appended before the extension.  E.g.:

    >>> create_thumbnail[[128, 128], 'image.jpg']

    A new thumbnail image is created with the name image_thumbnail.jpg

    :param size: A tuple of the width and height of the image
    :param path: The path to the image file
    :return: None
    """
    image = Image.open[path]
    image.thumbnail[size]
    path = Path[path]
    name = path.stem + '_thumbnail' + path.suffix
    thumbnail_path = path.with_name[name]
    image.save[thumbnail_path]


def main[]:
    ts = time[]
    for image_path in Path['images'].iterdir[]:
        create_thumbnail[[128, 128], image_path]
    logging.info['Took %s', time[] - ts]


if __name__ == '__main__':
    main[]

Tập lệnh này lặp lại các đường dẫn trong thư mục

import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
8 và với mỗi đường dẫn, nó chạy hàm create_thumbnail. Chức năng này sử dụng Gối để mở hình ảnh, tạo hình thu nhỏ và lưu hình ảnh mới, nhỏ hơn có cùng tên với hình gốc nhưng có thêm
import logging
import os
from functools import partial
from multiprocessing.pool import Pool
from time import time

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    download = partial[download_link, download_dir]
    with Pool[4] as p:
        p.map[download, links]
    logging.info['Took %s seconds', time[] - ts]


if __name__ == '__main__':
    main[]
9 vào tên

Chạy tập lệnh này trên 160 hình ảnh với tổng số 36 triệu mất 2. 32 giây. Hãy xem liệu chúng ta có thể tăng tốc độ này bằng cách sử dụng

import logging
from pathlib import Path
from time import time
from functools import partial

from concurrent.futures import ProcessPoolExecutor

from PIL import Image

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


def create_thumbnail[size, path]:
    """
    Creates a thumbnail of an image with the same name as image but with
    _thumbnail appended before the extension. E.g.:

    >>> create_thumbnail[[128, 128], 'image.jpg']

    A new thumbnail image is created with the name image_thumbnail.jpg

    :param size: A tuple of the width and height of the image
    :param path: The path to the image file
    :return: None
    """
    path = Path[path]
    name = path.stem + '_thumbnail' + path.suffix
    thumbnail_path = path.with_name[name]
    image = Image.open[path]
    image.thumbnail[size]
    image.save[thumbnail_path]


def main[]:
    ts = time[]
    # Partially apply the create_thumbnail method, setting the size to 128x128
    # and returning a function of a single argument.
    thumbnail_128 = partial[create_thumbnail, [128, 128]]

    # Create the executor in a with block so shutdown is called when the block
    # is exited.
    with ProcessPoolExecutor[] as executor:
        executor.map[thumbnail_128, Path['images'].iterdir[]]
    logging.info['Took %s', time[] - ts]


if __name__ == '__main__':
    main[]

Phương thức

import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
0 giống với tập lệnh cuối cùng. Sự khác biệt chính là việc tạo ra một
import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
1. Phương thức của người thi hành được sử dụng để tạo các hình thu nhỏ song song. Theo mặc định,
import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
1 tạo một quy trình con cho mỗi CPU. Chạy tập lệnh này trên cùng 160 hình ảnh mất 1. 05 giây—2. nhanh gấp 2 lần

Không đồng bộ/Đang chờ [Python 3. 5+ chỉ]

Một trong những mục được yêu cầu nhiều nhất trong các nhận xét về bài viết gốc là ví dụ sử dụng mô-đun của Python 3. So với các ví dụ khác, có một số cú pháp Python mới có thể mới đối với hầu hết mọi người và cũng có một số khái niệm mới. Một lớp phức tạp bổ sung đáng tiếc là do mô-đun

import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
3 tích hợp sẵn của Python không đồng bộ. Chúng tôi sẽ cần sử dụng thư viện HTTP async để nhận được đầy đủ lợi ích của asyncio. Đối với điều này, chúng tôi sẽ sử dụng aiohttp

Hãy nhảy ngay vào mã và một lời giải thích chi tiết hơn sẽ theo sau

import asyncio
import logging
import os
from time import time

import aiohttp

from download import setup_download_dir, get_links

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]


async def async_download_link[session, directory, link]:
    """
    Async version of the download_link method we've been using in the other examples.
    :param session: aiohttp ClientSession
    :param directory: directory to save downloads
    :param link: the url of the link to download
    :return:
    """
    download_path = directory / os.path.basename[link]
    async with session.get[link] as response:
        with download_path.open['wb'] as f:
            while True:
                # await pauses execution until the 1024 [or less] bytes are read from the stream
                chunk = await response.content.read[1024]
                if not chunk:
                    # We are done reading the file, break out of the while loop
                    break
                f.write[chunk]
    logger.info['Downloaded %s', link]


# Main is now a coroutine
async def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    # We use a session to take advantage of tcp keep-alive
    # Set a 3 second read and connect timeout. Default is 5 minutes
    async with aiohttp.ClientSession[conn_timeout=3, read_timeout=3] as session:
        tasks = [[async_download_link[session, download_dir, l]] for l in get_links[client_id]]
        # gather aggregates all the tasks and schedules them in the event loop
        await asyncio.gather[*tasks, return_exceptions=True]


if __name__ == '__main__':
    ts = time[]
    # Create the asyncio event loop
    loop = asyncio.get_event_loop[]
    try:
        loop.run_until_complete[main[]]
    finally:
        # Shutdown the loop even if there is an exception
        loop.close[]
    logger.info['Took %s seconds to complete', time[] - ts]

Có khá nhiều thứ để giải nén ở đây. Hãy bắt đầu với điểm vào chính của chương trình. Điều mới đầu tiên chúng tôi làm với mô-đun asyncio là lấy vòng lặp sự kiện. Vòng lặp sự kiện xử lý tất cả mã không đồng bộ. Sau đó, vòng lặp được chạy cho đến khi hoàn thành và chuyển hàm

import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
4. Có một phần cú pháp mới trong định nghĩa của main.
import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
5. Bạn cũng sẽ nhận thấy
import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
6 và
import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
7

Cú pháp async/await đã được giới thiệu trong PEP492. Cú pháp

import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
5 đánh dấu một hàm là một. Bên trong, các coroutine dựa trên các trình tạo Python, nhưng không hoàn toàn giống nhau. Các coroutine trả về một đối tượng coroutine tương tự như cách các trình tạo trả về một đối tượng trình tạo. Khi bạn có một coroutine, bạn sẽ nhận được kết quả của nó với biểu thức
import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
6. Khi một quy trình đăng ký gọi
import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
6, việc thực hiện quy trình đăng ký bị đình chỉ cho đến khi quá trình chờ đợi hoàn tất. Việc đình chỉ này cho phép các công việc khác được hoàn thành trong khi coroutine bị đình chỉ “chờ đợi” một số kết quả. Nói chung, kết quả này sẽ là một số loại I/O như yêu cầu cơ sở dữ liệu hoặc trong trường hợp của chúng tôi là yêu cầu HTTP

Hàm

import logging
import os
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logger = logging.getLogger[__name__]

def main[]:
    ts = time[]
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    for link in links:
        download_link[download_dir, link]
    logging.info['Took %s seconds', time[] - ts]

if __name__ == '__main__':
    main[]
1 phải được thay đổi khá nhiều. Trước đây, chúng tôi đã dựa vào
import logging
import os

from redis import Redis

from rq import Queue

from download import setup_download_dir, get_links, download_link


logging.basicConfig[level=logging.DEBUG, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']
logging.getLogger['requests'].setLevel[logging.CRITICAL]
logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]
    q = Queue[connection=Redis[host='localhost', port=6379]]
    for link in links:
        q.enqueue[download_link, download_dir, link]

if __name__ == '__main__':
    main[]
3 để thực hiện phần lớn công việc đọc hình ảnh cho chúng tôi. Bây giờ, để cho phép phương pháp của chúng tôi hoạt động chính xác với mô hình lập trình không đồng bộ, chúng tôi đã giới thiệu một vòng lặp
import logging
import os
from concurrent.futures import ThreadPoolExecutor
from functools import partial
from time import time

from download import setup_download_dir, get_links, download_link

logging.basicConfig[level=logging.INFO, format='%[asctime]s - %[name]s - %[levelname]s - %[message]s']

logger = logging.getLogger[__name__]


def main[]:
    client_id = os.getenv['IMGUR_CLIENT_ID']
    if not client_id:
        raise Exception["Couldn't find IMGUR_CLIENT_ID environment variable!"]
    download_dir = setup_download_dir[]
    links = get_links[client_id]

    # By placing the executor inside a with block, the executors shutdown method
    # will be called cleaning up threads.
    # 
    # By default, the executor sets number of workers to 5 times the number of
    # CPUs.
    with ThreadPoolExecutor[] as executor:

        # Create a new partially applied function that stores the directory
        # argument.
        # 
        # This allows the download_link function that normally takes two
        # arguments to work with the map function that expects a function of a
        # single argument.
        fn = partial[download_link, download_dir]

        # Executes fn concurrently using threads on the links iterable. The
        # timeout is for the entire process, not a single call, so downloading
        # all images must complete within 30 seconds.
        executor.map[fn, links, timeout=30]


if __name__ == '__main__':
    main[]
3 đọc các đoạn hình ảnh tại một thời điểm và tạm dừng thực thi trong khi chờ I/O hoàn tất. Điều này cho phép vòng lặp sự kiện lặp qua việc tải xuống các hình ảnh khác nhau vì mỗi hình ảnh đều có sẵn dữ liệu mới trong quá trình tải xuống

Nên có một—Tốt nhất là chỉ có một—Cách rõ ràng để làm điều đó

Mặc dù zen của Python cho chúng ta biết nên có một cách rõ ràng để làm điều gì đó, nhưng có nhiều cách trong Python để đưa đồng thời vào các chương trình của chúng ta. Phương pháp tốt nhất để chọn sẽ phụ thuộc vào trường hợp sử dụng cụ thể của bạn. Mô hình không đồng bộ chia tỷ lệ tốt hơn cho khối lượng công việc đồng thời cao [như máy chủ web] so với phân luồng hoặc đa xử lý, nhưng nó yêu cầu mã của bạn [và các phần phụ thuộc] không đồng bộ để có thể hưởng lợi đầy đủ

Hy vọng rằng các ví dụ về đa luồng Python trong bài viết này—và bản cập nhật—sẽ chỉ cho bạn đi đúng hướng để bạn có ý tưởng về nơi tìm kiếm trong thư viện chuẩn Python nếu bạn cần giới thiệu đồng thời vào các chương trình của mình

Đọc thêm trên Blog Kỹ thuật Toptal

  • Đồng thời và song song của Ruby. Hướng dẫn thực hành
  • Bài báo còn thiếu về đa luồng Qt trong C++
  • Ghi nhật ký Python. Hướng dẫn chuyên sâu
  • Luồng Android. Tất cả những gì bạn cần biết
  • Phối hợp một quy trình công việc nền trong Celery cho Python

Hiểu những điều cơ bản

Chuỗi trong Python là gì?

Một chủ đề là một quá trình hoặc nhiệm vụ nhẹ. Chuỗi là một cách để thêm đồng thời vào chương trình của bạn. Nếu ứng dụng Python của bạn đang sử dụng nhiều luồng và bạn xem các quy trình đang chạy trên hệ điều hành của mình, bạn sẽ chỉ thấy một mục duy nhất cho tập lệnh của mình mặc dù nó đang chạy nhiều luồng

Đa luồng là gì?

Đa luồng [đôi khi chỉ đơn giản là "phân luồng"] là khi một chương trình tạo nhiều luồng với chu kỳ thực thi giữa chúng, do đó, một tác vụ chạy lâu hơn không chặn tất cả các tác vụ khác. Điều này hoạt động tốt cho các nhiệm vụ có thể được chia thành các nhiệm vụ nhỏ hơn, sau đó mỗi nhiệm vụ có thể được trao cho một luồng để hoàn thành

Sự khác biệt giữa luồng Python và đa xử lý là gì?

Với luồng, đồng thời đạt được bằng cách sử dụng nhiều luồng, nhưng do GIL, chỉ có một luồng có thể chạy tại một thời điểm. Trong đa xử lý, quy trình ban đầu được chia nhỏ quy trình thành nhiều quy trình con bỏ qua GIL. Mỗi tiến trình con sẽ có một bản sao toàn bộ bộ nhớ của chương trình

Đa luồng và đa xử lý trong Python có liên quan như thế nào?

Cả đa luồng và đa xử lý đều cho phép mã Python chạy đồng thời. Chỉ đa xử lý mới cho phép mã của bạn thực sự song song. Tuy nhiên, nếu mã của bạn nặng về IO [như yêu cầu HTTP], thì đa luồng vẫn có thể tăng tốc mã của bạn

thẻ

PythonConcurrencyParallelismThreadingMultiprocessing

Người làm việc tự do? Tìm công việc tiếp theo của bạn.

Việc làm Lập trình viên Python

Xem thông tin đầy đủ

Marcus McCurdy

Kỹ sư phần mềm tự do

Thông tin về các Tác giả

Marcus có bằng cử nhân kỹ thuật máy tính và bằng thạc sĩ khoa học máy tính. Anh ấy là một lập trình viên tài năng và xuất sắc nhất trong lĩnh vực phát triển back-end, nhưng anh ấy cũng hoàn toàn thoải mái khi tạo ra các sản phẩm bóng bẩy với tư cách là một nhà phát triển full-stack

Thuê Marcus

Bình luận

jorjun

Muốn xem một ví dụ sử dụng mô-đun asyncio mới. Ngoài ra, Gevent hoạt động kỳ diệu đối với loại sự cố đồng thời bị ràng buộc I/O này nếu bạn phải gắn bó với python 2. Thực sự không cần sử dụng luồng hoặc đa xử lý - luồng xanh có thể là cách tốt nhất

jorjun

Muốn xem một ví dụ sử dụng mô-đun asyncio mới. Ngoài ra, Gevent hoạt động kỳ diệu đối với loại sự cố đồng thời bị ràng buộc I/O này nếu bạn phải gắn bó với python 2. Thực sự không cần sử dụng luồng hoặc đa xử lý - luồng xanh có thể là cách tốt nhất

Marcus McCurdy

Mô-đun asyncio sẽ là một ví dụ khá quan trọng vì mã urllib cơ bản không được thiết lập để sử dụng các kết nối không đồng bộ. Bạn có thể xem một ví dụ ở đây https. // tài liệu. con trăn. org/3/library/asyncio-stream. html#get-http-headers của tìm nạp tiêu đề. Tôi định chạm vào Gevent, nhưng nó không hoạt động với Python 3 vào lúc này

Marcus McCurdy

Mô-đun asyncio sẽ là một ví dụ khá quan trọng vì mã urllib cơ bản không được thiết lập để sử dụng các kết nối không đồng bộ. Bạn có thể xem một ví dụ ở đây https. // tài liệu. con trăn. org/3/library/asyncio-stream. html#get-http-headers của tìm nạp tiêu đề. Tôi định chạm vào Gevent, nhưng nó không hoạt động với Python 3 vào lúc này

Mariano Simone

Nếu mã của bạn bị ràng buộc I/O, bạn nên tăng số lượng luồng dữ liệu mà một quy trình quản lý bằng I/O không đồng bộ. Tạo một luồng/rẽ một quy trình chỉ để xử lý các kết nối mới là một sự lãng phí tài nguyên khủng khiếp. Bây giờ, nếu bạn đang thực hiện các hoạt động chuyên sâu về CPU, thì rõ ràng việc ném nhiều lõi hơn vào vấn đề là điều hợp lý.

Mariano Simone

Nếu mã của bạn bị ràng buộc I/O, bạn nên tăng số lượng luồng dữ liệu mà một quy trình quản lý bằng I/O không đồng bộ. Tạo một luồng/rẽ một quy trình chỉ để xử lý các kết nối mới là một sự lãng phí tài nguyên khủng khiếp. Bây giờ, nếu bạn đang thực hiện các hoạt động chuyên sâu về CPU, thì rõ ràng việc ném nhiều lõi hơn vào vấn đề là điều hợp lý.

Mariano Simone

Btw, số lượng công việc và chất lượng của bài đăng rất ấn tượng. Dude công việc tốt

Mariano Simone

Btw, số lượng công việc và chất lượng của bài đăng rất ấn tượng. Dude công việc tốt

jorjun

Tôi thấy. Mô-đun aiohttp có vẻ đầy hứa hẹn. http. // geekgirl. io/concurrent-http-requests-with-python3-and-asyncio/

jorjun

Tôi thấy. Mô-đun aiohttp có vẻ đầy hứa hẹn. http. // geekgirl. io/concurrent-http-requests-with-python3-and-asyncio/

Marcus McCurdy

Bạn đúng rằng cách tiếp cận đồng thời/song song phụ thuộc vào việc mã cơ bản có phải là IO so với. CPU bị ràng buộc và tôi chạm vào điều này một chút trong bài viết. Tôi muốn giữ cho bài viết đơn giản nhất có thể và vẫn thể hiện các tùy chọn khác nhau có sẵn trong Python 3. Tôi cảm thấy rằng việc bao gồm cả các ví dụ về giới hạn IO và giới hạn CPU sẽ làm bài viết trở nên cồng kềnh, nhưng tôi có đề cập đến một ví dụ về tác vụ giới hạn CPU trong bài viết. Tôi thực sự muốn viết bài này bằng Python 3 vì tôi cảm thấy không có nhiều tài nguyên cho nó. Tôi cũng muốn bao gồm IO không đồng bộ bằng cách sử dụng gevent, nhưng gevent không hỗ trợ Python 3. Tôi quyết định sử dụng một bài viết Python 3 thuần túy thay vì bao gồm một số ví dụ hoạt động trong Python 3 và một số hoạt động trong Python 2

Marcus McCurdy

Bạn đúng rằng cách tiếp cận đồng thời/song song phụ thuộc vào việc mã cơ bản có phải là IO so với. CPU bị ràng buộc và tôi chạm vào điều này một chút trong bài viết. Tôi muốn giữ cho bài viết đơn giản nhất có thể và vẫn thể hiện các tùy chọn khác nhau có sẵn trong Python 3. Tôi cảm thấy rằng việc bao gồm cả các ví dụ về giới hạn IO và giới hạn CPU sẽ làm bài viết trở nên cồng kềnh, nhưng tôi có đề cập đến một ví dụ về tác vụ giới hạn CPU trong bài viết. Tôi thực sự muốn viết bài này bằng Python 3 vì tôi cảm thấy không có nhiều tài nguyên cho nó. Tôi cũng muốn bao gồm IO không đồng bộ bằng cách sử dụng gevent, nhưng gevent không hỗ trợ Python 3. Tôi quyết định sử dụng một bài viết Python 3 thuần túy thay vì bao gồm một số ví dụ hoạt động trong Python 3 và một số hoạt động trong Python 2

Marcus McCurdy

Cảm ơn lời khen và bình luận khác của bạn

Marcus McCurdy

Cảm ơn lời khen và bình luận khác của bạn

Eki Eqbal

Bài viết hay, tiếp tục phát huy. Chúc mừng

Eki Eqbal

Bài viết hay, tiếp tục phát huy. Chúc mừng

rockqi

bài viết hay với hình ảnh thú vị, cảm ơn

rockqi

bài viết hay với hình ảnh thú vị, cảm ơn

Zero_NzYme

bài viết rất hay. Sẽ thử các tùy chọn redis và cần tây. Rất tuyệt

Zero_NzYme

bài viết rất hay. Sẽ thử các tùy chọn redis và cần tây. Rất tuyệt

Jon

Xin chào, bạn có thể cung cấp một số thông tin chi tiết về cách một người nào đó có thể sử dụng tính năng đa xử lý để thực hiện các chức năng khác nhau không?. g. chức năng đầu tiên là đọc các tệp csv đã nén vào bộ nhớ và chức năng thứ hai hợp nhất các tệp csv đó theo cùng một thứ tự. Bằng cách này, chức năng 2 vẫn đang hợp nhất tệp 1 và tệp 2, tệp 3 đang được đọc vào bộ nhớ. Tương tự như cách một cửa hàng bánh sandwich có thể có nhiều công nhân làm việc trên cùng một chiếc bánh mì khi họ trải qua các giai đoạn sản xuất khác nhau

Jon

Xin chào, bạn có thể cung cấp một số thông tin chi tiết về cách một người nào đó có thể sử dụng tính năng đa xử lý để thực hiện các chức năng khác nhau không?. g. chức năng đầu tiên là đọc các tệp csv đã nén vào bộ nhớ và chức năng thứ hai hợp nhất các tệp csv đó theo cùng một thứ tự. Bằng cách này, chức năng 2 vẫn đang hợp nhất tệp 1 và tệp 2, tệp 3 đang được đọc vào bộ nhớ. Tương tự như cách một cửa hàng bánh sandwich có thể có nhiều công nhân làm việc trên cùng một chiếc bánh mì khi họ trải qua các giai đoạn sản xuất khác nhau

Ashwin Nanjappa

Bài báo hay. Tuy nhiên, nó không được đề cập phải làm gì nếu mã của bạn bị ràng buộc bởi CPU, dữ liệu lớn [đa xử lý không còn nữa] và không phải là một ứng dụng web. Đây là điển hình của các ứng dụng khoa học. Bài báo ít nhất nên đề cập trong phần kết luận rằng điều này hiện không thể được giải quyết trong CPython một cách hiệu quả

Ashwin Nanjappa

Bài báo hay. Tuy nhiên, nó không được đề cập phải làm gì nếu mã của bạn bị ràng buộc bởi CPU, dữ liệu lớn [đa xử lý không còn nữa] và không phải là một ứng dụng web. Đây là điển hình của các ứng dụng khoa học. Bài báo ít nhất nên đề cập trong phần kết luận rằng điều này hiện không thể được giải quyết trong CPython một cách hiệu quả

Anon Omus

Hiển thị một kỹ thuật mà không cung cấp thông tin cần thiết để sử dụng đúng kỹ thuật khó có thể được coi là phình to. Nó chỉ là lười biếng. . /

Anon Omus

Hiển thị một kỹ thuật mà không cung cấp thông tin cần thiết để sử dụng đúng kỹ thuật khó có thể được coi là phình to. Nó chỉ là lười biếng. . /

Andrew Franklin

Là một người không biết nhiều về đồng thời và song song trong python, đây có vẻ là một nơi tốt để bắt đầu. Cảm ơn cho hướng dẫn này. Thật không may, tôi cũng không biết nhiều về urllib và tải hình ảnh từ imgur. Đôi khi, các mã này không chạy cho tôi và tôi gặp lỗi sau. "urllib. lỗi. Lỗi HTTP. Lỗi HTTP 403. Quyền bị từ chối" Rất không nhất quán khi tôi nhận được lỗi và đôi khi mã chạy mà không gặp trở ngại nào. Có ai biết cách sửa lỗi này để nó chạy mọi lúc không?

Andrew Franklin

Là một người không biết nhiều về đồng thời và song song trong python, đây có vẻ là một nơi tốt để bắt đầu. Cảm ơn cho hướng dẫn này. Thật không may, tôi cũng không biết nhiều về urllib và tải hình ảnh từ imgur. Đôi khi, các mã này không chạy cho tôi và tôi gặp lỗi sau. "urllib. lỗi. Lỗi HTTP. Lỗi HTTP 403. Quyền bị từ chối" Rất không nhất quán khi tôi nhận được lỗi và đôi khi mã chạy mà không gặp trở ngại nào. Có ai biết cách sửa lỗi này để nó chạy mọi lúc không?

Eric O LEBIGOT [EOL]

Làm tốt lắm. hiếm thấy những blogger viết mã Python giỏi. . ] Bây giờ, bạn chia sẻ tốc độ tăng tốc thu được với chủ đề. bạn nhận được gì với đa xử lý?

Eric O LEBIGOT [EOL]

Làm tốt lắm. hiếm thấy những blogger viết mã Python giỏi. . ] Bây giờ, bạn chia sẻ tốc độ tăng tốc thu được với chủ đề. bạn nhận được gì với đa xử lý?

mattias

Xin chào, Phần phân luồng cho số lượng CPU không có ý nghĩa gì. Vì nó sẽ chỉ chạy trên một lõi CPU vì các luồng python không phải là luồng os thực nên không liên quan đến số lượng lõi có sẵn

mattias

Xin chào, Phần phân luồng cho số lượng CPU không có ý nghĩa gì. Vì nó sẽ chỉ chạy trên một lõi CPU vì các luồng python không phải là luồng os thực nên không liên quan đến số lượng lõi có sẵn

Yuval Baror

Bai bao tuyệt vơi, cảm ơn vi đa chia sẻ. Bạn có thể cung cấp một số chi tiết về thời gian chạy của các giải pháp đa xử lý và RQ so với các giải pháp ban đầu và theo luồng không?

Yuval Baror

Bai bao tuyệt vơi, cảm ơn vi đa chia sẻ. Bạn có thể cung cấp một số chi tiết về thời gian chạy của các giải pháp đa xử lý và RQ so với các giải pháp ban đầu và theo luồng không?

Marcus McCurdy

Bạn có thể tìm thấy mã nguồn đầy đủ của tất cả các ví dụ trong bài viết tại đây https. //github. com/volker48/python-concurrency. Tôi chưa bao giờ thấy bất kỳ lỗi 403 nào khi thử nghiệm, nhưng lỗi đó sẽ đến từ API của Imgur. Tôi không chắc bạn đang sử dụng loại mạng nào, nhưng có lẽ IP của bạn, nếu được chia sẻ, sẽ khiến bạn đạt đến một số loại giới hạn API

Marcus McCurdy

Bạn có thể tìm thấy mã nguồn đầy đủ của tất cả các ví dụ trong bài viết tại đây https. //github. com/volker48/python-concurrency. Tôi chưa bao giờ thấy bất kỳ lỗi 403 nào khi thử nghiệm, nhưng lỗi đó sẽ đến từ API của Imgur. Tôi không chắc bạn đang sử dụng loại mạng nào, nhưng có lẽ IP của bạn, nếu được chia sẻ, sẽ khiến bạn đạt đến một số loại giới hạn API

thợ xóa

Tôi cũng muốn xem kết quả này. Bạn có thể vui lòng thêm nó vào bài đăng hoặc ở đây như một câu trả lời không?

thợ xóa

Tôi cũng muốn xem kết quả này. Bạn có thể vui lòng thêm nó vào bài đăng hoặc ở đây như một câu trả lời không?

ravi

bài viết hay. Ngạc nhiên khi không thấy Gevent trong danh sách

ravi

bài viết hay. Ngạc nhiên khi không thấy Gevent trong danh sách

bjlange

+1 cho RQ. Cho đến nay, cách thân thiện với người dùng nhất mà tôi đã tìm thấy để phân phối công việc trên các máy

bjlange

+1 cho RQ. Cho đến nay, cách thân thiện với người dùng nhất mà tôi đã tìm thấy để phân phối công việc trên các máy

Nabeel Valapra

Đây là những gì tôi đang tìm kiếm. Người tuyệt vời

Nabeel Valapra

Đây là những gì tôi đang tìm kiếm. Người tuyệt vời

Jay Dreyer

Cám ơn vì cái này. Cải tiến gấp 10 lần trong tập lệnh tôi sử dụng thường xuyên. Thanks

Jay Dreyer

Cám ơn vì cái này. Cải tiến gấp 10 lần trong tập lệnh tôi sử dụng thường xuyên. Thanks

JEdVcM

Trong mã luồng của bạn, bạn đang chờ đợi bận rộn với "trong khi đúng" + đọc hàng đợi không chặn. Điều này không sao nếu bạn có nội dung trong hàng đợi của mình và bạn kết thúc nội dung đó ở cuối, nhưng nếu không thì nó sẽ trở thành một con lợn CPU. Chúng tôi đã có điều đó một vài tháng trước trong dự án của chúng tôi. Một điều khác, tôi chắc chắn không hiểu hoàn toàn về luồng python, nhưng ngay cả trong môi trường ràng buộc I/O, làm thế nào bạn có thể nhanh hơn nếu bạn chỉ sử dụng các lần đọc mạng đồng bộ? . Tôi bỏ lỡ điều gì?

JEdVcM

Trong mã luồng của bạn, bạn đang chờ đợi bận rộn với "trong khi đúng" + đọc hàng đợi không chặn. Điều này không sao nếu bạn có nội dung trong hàng đợi của mình và bạn kết thúc nội dung đó ở cuối, nhưng nếu không thì nó sẽ trở thành một con lợn CPU. Chúng tôi đã có điều đó một vài tháng trước trong dự án của chúng tôi. Một điều khác, tôi chắc chắn không hiểu hoàn toàn về luồng python, nhưng ngay cả trong môi trường ràng buộc I/O, làm thế nào bạn có thể nhanh hơn nếu bạn chỉ sử dụng các lần đọc mạng đồng bộ? . Tôi bỏ lỡ điều gì?

Dannnno

Theo những gì tôi nhớ lại, CPython phát hành GIL khi các luồng đang chờ sự kiện IO - tuy nhiên tôi không rõ chi tiết cụ thể về cách thức thực hiện

Dannnno

Theo những gì tôi nhớ lại, CPython phát hành GIL khi các luồng đang chờ sự kiện IO - tuy nhiên tôi không rõ chi tiết cụ thể về cách thức thực hiện

JEdVcM

Điều đó sẽ giải thích mọi thứ tốt. Tuy nhiên, tôi chưa tìm thấy gì về điều đó. Cái khác. xin lỗi, tôi đã sai. Mã này sử dụng chờ chặn

JEdVcM

Điều đó sẽ giải thích mọi thứ tốt. Tuy nhiên, tôi chưa tìm thấy gì về điều đó. Cái khác. xin lỗi, tôi đã sai. Mã này sử dụng chờ chặn

Dannnno

Tôi cũng không thể tìm thấy nơi tôi đã đọc nó ban đầu, tuy nhiên bạn có thể có mẩu tin nhỏ này "Lưu ý rằng các hoạt động có khả năng chặn hoặc chạy dài, chẳng hạn như I/O, xử lý hình ảnh và xử lý số NumPy, xảy ra bên ngoài GIL. " https. //wiki. con trăn. org/moin/GlobalInterpreterLock

Dannnno

Tôi cũng không thể tìm thấy nơi tôi đã đọc nó ban đầu, tuy nhiên bạn có thể có mẩu tin nhỏ này "Lưu ý rằng các hoạt động có khả năng chặn hoặc chạy dài, chẳng hạn như I/O, xử lý hình ảnh và xử lý số NumPy, xảy ra bên ngoài GIL. " https. //wiki. con trăn. org/moin/GlobalInterpreterLock

Arulbalan Subramani

Xin chào Marcus, Bài đăng tuyệt vời. Cảm ơn, một câu hỏi. mặc dù chúng tôi không có chủ đề nào được kiểm soát bằng cách sử dụng lớp semaphore, tại sao chúng tôi cần lớp hàng đợi để xử lý các chủ đề?

Arulbalan Subramani

Xin chào Marcus, Bài đăng tuyệt vời. Cảm ơn, một câu hỏi. mặc dù chúng tôi không có chủ đề nào được kiểm soát bằng cách sử dụng lớp semaphore, tại sao chúng tôi cần lớp hàng đợi để xử lý các chủ đề?

Quảng trường Andreu Vallbona

Trong ví dụ về Pool đa xử lý, làm thế nào để bạn kiểm soát cùng một liên kết không được tải xuống cho mọi quy trình?

Quảng trường Andreu Vallbona

Trong ví dụ về Pool đa xử lý, làm thế nào để bạn kiểm soát cùng một liên kết không được tải xuống cho mọi quy trình?

Marcus McCurdy

Its the way the map function works with the multiprocessing pool.

p.map[download, links]
Links is a list of download links. p.map calls the download function once for each link in the list. Because this is the map function on a pool object each function may run in its own process. Pool's map function works similarly to Python's built in map function. Not sure if you are familiar with it, but it might make more sense if you play around with it //docs.python.org/2/library/functions.html#map.

Marcus McCurdy

Its the way the map function works with the multiprocessing pool.

p.map[download, links]
Links is a list of download links. p.map calls the download function once for each link in the list. Because this is the map function on a pool object each function may run in its own process. Pool's map function works similarly to Python's built in map function. Not sure if you are familiar with it, but it might make more sense if you play around with it //docs.python.org/2/library/functions.html#map.

Mike Zang

Đây là một công việc tuyệt vời. Tôi muốn sử dụng nó cho các dự án của tôi. Tôi phân tích cú pháp từng liên kết hình ảnh một cách linh hoạt, làm cách nào tôi có thể sử dụng cách chuỗi hoặc nhóm của bạn?

Mike Zang

Đây là một công việc tuyệt vời. Tôi muốn sử dụng nó cho các dự án của tôi. Tôi phân tích cú pháp từng liên kết hình ảnh một cách linh hoạt, làm cách nào tôi có thể sử dụng cách chuỗi hoặc nhóm của bạn?

bãi cát

Cần tây là một thứ tuyệt vời để sử dụng. Nó có thể mở rộng quy mô thực sự tốt. Ngoài ra, nó có thể linh hoạt sử dụng redis hoặc rabbitmq làm nhà môi giới

bãi cát

Cần tây là một thứ tuyệt vời để sử dụng. Nó có thể mở rộng quy mô thực sự tốt. Ngoài ra, nó có thể linh hoạt sử dụng redis hoặc rabbitmq làm nhà môi giới

bia3andro

Cám ơn bạn rất nhiều về điều này. Nỗ lực đầu tiên của tôi để viết một tập lệnh python theo luồng đã dẫn đến thành công

bia3andro

Cám ơn bạn rất nhiều về điều này. Nỗ lực đầu tiên của tôi để viết một tập lệnh python theo luồng đã dẫn đến thành công

Sunda Môsê

Marcus viết rất hay. Tôi cũng là một lập trình viên python. Mặc dù tôi đã sử dụng các chức năng này đồng thời, nhưng tôi không thể viết lại chương trình bằng python vào lần tới và tôi quên luồng hoặc tham số. một lần nữa tôi giới thiệu chương trình cũ của mình để nhớ lại dòng chảy. Bạn có mẹo nào để ghi nhớ các lệnh gọi hàm và đối số này không?

Sunda Môsê

Marcus viết rất hay. Tôi cũng là một lập trình viên python. Mặc dù tôi đã sử dụng các chức năng này đồng thời, nhưng tôi không thể viết lại chương trình bằng python vào lần tới và tôi quên luồng hoặc tham số. một lần nữa tôi giới thiệu chương trình cũ của mình để nhớ lại dòng chảy. Bạn có mẹo nào để ghi nhớ các lệnh gọi hàm và đối số này không?

máy bay

Xin chào, tôi có chức năng chấp nhận đường dẫn tệp và thực hiện phân tích trên đó. Nó trả về một id cho hàng khung dữ liệu gấu trúc mà nó đã được thêm vào. Đường dẫn tệp được truyền dưới dạng một chuỗi mỗi lần, từ một chương trình khác. Theo thời gian, phân tích đã bao gồm các loại tệp khác nhau và mất một thời gian. Tôi cần triển khai đa xử lý cho phần này. Điều gì sẽ là cách tốt nhất để làm điều đó?

máy bay

Xin chào, tôi có chức năng chấp nhận đường dẫn tệp và thực hiện phân tích trên đó. Nó trả về một id cho hàng khung dữ liệu gấu trúc mà nó đã được thêm vào. Đường dẫn tệp được truyền dưới dạng một chuỗi mỗi lần, từ một chương trình khác. Theo thời gian, phân tích đã bao gồm các loại tệp khác nhau và mất một thời gian. Tôi cần triển khai đa xử lý cho phần này. Điều gì sẽ là cách tốt nhất để làm điều đó?

Will Vaughn

Vâng, mã của bạn trên github hoạt động, nhưng những gì bạn đã viết trong blog không hoạt động

Will Vaughn

Vâng, mã của bạn trên github hoạt động, nhưng những gì bạn đã viết trong blog không hoạt động

Marcus McCurdy

Mã trên github đã được cập nhật sau khi bài đăng trên blog được viết để giải thích cho một số thay đổi trong API imgur. Tôi không có quyền truy cập trực tiếp vào bài đăng trên blog như tôi làm với repo github. Phần nào của bài đăng trên blog không phù hợp với bạn? . cảm ơn sẽ

Marcus McCurdy

Mã trên github đã được cập nhật sau khi bài đăng trên blog được viết để giải thích cho một số thay đổi trong API imgur. Tôi không có quyền truy cập trực tiếp vào bài đăng trên blog như tôi làm với repo github. Phần nào của bài đăng trên blog không phù hợp với bạn? . cảm ơn sẽ

David Nguyễn

Bài báo tuyệt vời

David Nguyễn

Bài báo tuyệt vời

Rohit Malgaonkar

Chưa thử máy chủ redis nhưng đã cài đặt máy chủ python RabbitMQ và có 1 nhà sản xuất [gửi dữ liệu chuỗi được mã hóa dưới dạng JSON] và 3 người tiêu dùng cho tập lệnh ràng buộc cpu [lặp tệp csv, tìm kiếm dữ liệu JSON được giải mã do nhà sản xuất gửi] và nó giảm một nửa thời gian nhưng

Rohit Malgaonkar

Chưa thử máy chủ redis nhưng đã cài đặt máy chủ python RabbitMQ và có 1 nhà sản xuất [gửi dữ liệu chuỗi được mã hóa dưới dạng JSON] và 3 người tiêu dùng cho tập lệnh ràng buộc cpu [lặp tệp csv, tìm kiếm dữ liệu JSON được giải mã do nhà sản xuất gửi] và nó giảm một nửa thời gian nhưng

Vikas Gupta

Blog tốt về đa luồng. Thanks. . ]

Vikas Gupta

Blog tốt về đa luồng. Thanks. . ]

Dung nham Kafle

tuyệt quá

Dung nham Kafle

tuyệt quá

Heron Rossi

bài viết rất hay. Mục đích của công nhân là gì. daemon = True cho ví dụ cụ thể này? . tham gia[]] ?

Heron Rossi

bài viết rất hay. Mục đích của công nhân là gì. daemon = True cho ví dụ cụ thể này? . tham gia[]] ?

Daniel Nuriev

tôi đồng ý. Tác giả và độc giả thích bài viết này không nhận ra rằng các luồng không phải là luồng của hệ điều hành mà là các chuỗi mã byte Python chạy trên một luồng hệ điều hành. Python VM thực thi từng luồng tối đa 10 mili giây và chuyển sang luồng tiếp theo. Sự cải tiến là do mã này thực hiện IO. Không có IO, hiệu suất sẽ kém hơn

Daniel Nuriev

tôi đồng ý. Tác giả và độc giả thích bài viết này không nhận ra rằng các luồng không phải là luồng của hệ điều hành mà là các chuỗi mã byte Python chạy trên một luồng hệ điều hành. Python VM thực thi từng luồng tối đa 10 mili giây và chuyển sang luồng tiếp theo. Sự cải tiến là do mã này thực hiện IO. Không có IO, hiệu suất sẽ kém hơn

mattias

Tôi định nói với bạn rằng cả hai bạn đều sai khi tôi nhận ra rằng tôi đã viết tuyên bố ban đầu. Dù sao, các luồng python là các luồng thực, được gắn với cùng một PPID, nhưng chỉ một luồng được thực thi tại một thời điểm bất kể số lượng lõi có sẵn do khóa interpeter toàn cầu. Có cái đó

mattias

Tôi định nói với bạn rằng cả hai bạn đều sai khi tôi nhận ra rằng tôi đã viết tuyên bố ban đầu. Dù sao, các luồng python là các luồng thực, được gắn với cùng một PPID, nhưng chỉ một luồng được thực thi tại một thời điểm bất kể số lượng lõi có sẵn do khóa interpeter toàn cầu. Có cái đó

Daniel Nuriev

cám ơn vì đã giải thích. Trong trường hợp này tôi có một câu hỏi khác. có đúng là nếu một luồng chạy hơn 10 mili giây, Python VM sẽ chuyển sang luồng tiếp theo để không bị kẹt không?

Daniel Nuriev

cám ơn vì đã giải thích. Trong trường hợp này tôi có một câu hỏi khác. có đúng là nếu một luồng chạy hơn 10 mili giây, Python VM sẽ chuyển sang luồng tiếp theo để không bị kẹt không?

mattias

Từ những gì tôi có thể google nhanh chóng, bất kỳ chuỗi liên kết cpu nào [nghĩa là không đợi i/o] đang giải phóng gil [và yêu cầu gửi lại. -mua nó] cứ sau 100 tích tắc. Tôi không chắc dấu tích chính xác là gì nhưng tôi nghi ngờ đó là chu kỳ cpu, mà đúng hơn là một số vòng lặp thực thi trong chính trình thông dịch, tôi đoán vậy. -]. Dù sao, điều này đảm bảo tất cả các luồng đều có thời gian thực hiện, nhưng hình phạt quá cao nên việc cố gắng thực hiện nhiều luồng cùng một lúc sẽ tệ hơn nhiều so với việc chạy các tác vụ một cách tuần tự. Tôi đã tìm thấy trang trình bày này giải thích lịch trình cho tôi, vì vậy hãy lấy nó để làm khách mời. ]. http. //www. dabeaz. com/python/Hiểu biếtGIL. pdf

mattias

Từ những gì tôi có thể google nhanh chóng, bất kỳ chuỗi liên kết cpu nào [nghĩa là không đợi i/o] đang giải phóng gil [và yêu cầu gửi lại. -mua nó] cứ sau 100 tích tắc. Tôi không chắc dấu tích chính xác là gì nhưng tôi nghi ngờ đó là chu kỳ cpu, mà đúng hơn là một số vòng lặp thực thi trong chính trình thông dịch, tôi đoán vậy. -]. Dù sao, điều này đảm bảo tất cả các luồng đều có thời gian thực hiện, nhưng hình phạt quá cao nên việc cố gắng thực hiện nhiều luồng cùng một lúc sẽ tệ hơn nhiều so với việc chạy các tác vụ một cách tuần tự. Tôi đã tìm thấy trang trình bày này giải thích lịch trình cho tôi, vì vậy hãy lấy nó để làm khách mời. ]. http. //www. dabeaz. com/python/Hiểu biếtGIL. pdf

Sở thích

Tôi cố gắng tạo lại mã này để hiểu về đa luồng. Nhưng tôi đã gặp lỗi khi tôi chạy nó. tiêu đề = {'Ủy quyền'. 'ID khách hàng {}'. định dạng [client_id]} -> đầu ra. Tiêu đề yêu cầu không hợp lệ = {'Ủy quyền'. 'ID ứng dụng khách {{}}'. định dạng [client_id]} -> đầu ra. Quyền bị từ chối Tôi có cần tải hình ảnh lên tài khoản imgur của mình không?

Sở thích

Tôi cố gắng tạo lại mã này để hiểu về đa luồng. Nhưng tôi đã gặp lỗi khi tôi chạy nó. tiêu đề = {'Ủy quyền'. 'ID khách hàng {}'. định dạng [client_id]} -> đầu ra. Tiêu đề yêu cầu không hợp lệ = {'Ủy quyền'. 'ID ứng dụng khách {{}}'. định dạng [client_id]} -> đầu ra. Quyền bị từ chối Tôi có cần tải hình ảnh lên tài khoản imgur của mình không?

Kevin

Tôi đã lấy ví dụ luồng ban đầu của bạn và viết một số mã hoạt động rất tốt. Tuy nhiên, nhiệm vụ là nó phải chạy bên trong máy chủ web và mặc dù nó chạy tốt như nhau ở đó, nhưng có thể nói đó là "các luồng tích lũy" vì python không thực sự thoát cho đến khi chính máy chủ web bị tắt. e. g. Tôi đang tạo ra tám luồng và mỗi khi điểm cuối được thực thi, nó sẽ sử dụng một bộ mới. Tôi biết điều này bởi vì tôi đang viết các tên luồng riêng lẻ [luồng. current_thread[]. getName[]] và tổng số vào tệp nhật ký. Điều này đúng bất kể tôi có đặt chúng là daemon hay không. Tôi không phải là bậc thầy về trăn. Bất kỳ đầu vào/đề xuất nào về cách tôi có thể liên tục sử dụng lại cùng một nhóm luồng [một khi được xác định trong lần thực thi ban đầu] hoặc cách khác là "xóa" các đối tượng luồng để chúng không tiếp tục tích lũy?

Kevin

Tôi đã lấy ví dụ luồng ban đầu của bạn và viết một số mã hoạt động rất tốt. Tuy nhiên, nhiệm vụ là nó phải chạy bên trong máy chủ web và mặc dù nó chạy tốt như nhau ở đó, nhưng có thể nói đó là "các luồng tích lũy" vì python không thực sự thoát cho đến khi chính máy chủ web bị tắt. e. g. Tôi đang tạo ra tám luồng và mỗi khi điểm cuối được thực thi, nó sẽ sử dụng một bộ mới. Tôi biết điều này bởi vì tôi đang viết các tên luồng riêng lẻ [luồng. current_thread[]. getName[]] và tổng số vào tệp nhật ký. Điều này đúng bất kể tôi có đặt chúng là daemon hay không. Tôi không phải là bậc thầy về trăn. Bất kỳ đầu vào/đề xuất nào về cách tôi có thể liên tục sử dụng lại cùng một nhóm luồng [một khi được xác định trong lần thực thi ban đầu] hoặc cách khác là "xóa" các đối tượng luồng để chúng không tiếp tục tích lũy?

Marcus McCurdy

Này Kevin, thật khó để nói chính xác cách hành động tốt nhất là gì nếu không biết thêm một chút về những gì có thể gọi được trong chuỗi của bạn đang làm. Nếu chủ đề của bạn giống như lớp DownloadWorker của tôi trong ví dụ đó có thể không phải là cách tiếp cận tốt nhất cho trường hợp sử dụng của bạn. Các tài liệu về lớp chủ đề https. // tài liệu. con trăn. tổ chức/3. 6/thư viện/luồng. html#thread-object nói. "Khi hoạt động của chuỗi được bắt đầu, chuỗi được coi là 'còn sống'. Nó ngừng hoạt động khi phương thức run[] của nó kết thúc - thông thường hoặc bằng cách đưa ra một ngoại lệ chưa được xử lý. Phương thức is_alive[] kiểm tra xem thread còn sống hay không. " vì vậy nếu phương thức chạy của bạn trên lớp kế thừa từ Chủ đề kết thúc và thoát khỏi luồng sẽ không còn tồn tại. Bạn có thể muốn kiểm tra ví dụ cập nhật của tôi sử dụng https. // tài liệu. con trăn. org/3/library/concurrent. tương lai. html#threadpoolexecutor. Với trình thực thi, bạn chọn số lượng chủ đề tối đa tạo nên nhóm. Sau đó, bạn gửi nhiệm vụ cho người thực thi, người sẽ sử dụng một trong các luồng có sẵn trong nhóm hoặc khối cho đến khi có sẵn một luồng. Khi bạn đã tạo trình thực thi của mình, bạn gọi phương thức gửi trên trình thực thi để tác vụ của bạn chạy không đồng bộ trên một trong các luồng. Gửi trả về một tương lai mà sau đó bạn có thể sử dụng để nhận kết quả. Tùy thuộc vào trường hợp sử dụng của bạn, bạn có thể không cần sử dụng tương lai gửi trả lại. ThreadPoolExecutor nghe giống như những gì bạn đang tìm kiếm vì bạn nói rằng bạn muốn "sử dụng lại cùng một bộ chủ đề" Hy vọng rằng điều này sẽ hữu ích. Nếu không, vui lòng gửi email một ý chính với một ví dụ về mã. email của tôi là tên của tôi dấu chấm họ tại toptal dot com

Marcus McCurdy

Này Kevin, thật khó để nói chính xác cách hành động tốt nhất là gì nếu không biết thêm một chút về những gì có thể gọi được trong chuỗi của bạn đang làm. Nếu chủ đề của bạn giống như lớp DownloadWorker của tôi trong ví dụ đó có thể không phải là cách tiếp cận tốt nhất cho trường hợp sử dụng của bạn. Các tài liệu về lớp chủ đề https. // tài liệu. con trăn. tổ chức/3. 6/thư viện/luồng. html#thread-object nói. "Khi hoạt động của chuỗi được bắt đầu, chuỗi được coi là 'còn sống'. Nó ngừng hoạt động khi phương thức run[] của nó kết thúc - thông thường hoặc bằng cách đưa ra một ngoại lệ chưa được xử lý. Phương thức is_alive[] kiểm tra xem thread còn sống hay không. " vì vậy nếu phương thức chạy của bạn trên lớp kế thừa từ Chủ đề kết thúc và thoát khỏi luồng sẽ không còn tồn tại. Bạn có thể muốn kiểm tra ví dụ cập nhật của tôi sử dụng https. // tài liệu. con trăn. org/3/library/concurrent. tương lai. html#threadpoolexecutor. Với trình thực thi, bạn chọn số lượng chủ đề tối đa tạo nên nhóm. Sau đó, bạn gửi nhiệm vụ cho người thực thi, người sẽ sử dụng một trong các luồng có sẵn trong nhóm hoặc khối cho đến khi có sẵn một luồng. Khi bạn đã tạo trình thực thi của mình, bạn gọi phương thức gửi trên trình thực thi để tác vụ của bạn chạy không đồng bộ trên một trong các luồng. Gửi trả về một tương lai mà sau đó bạn có thể sử dụng để nhận kết quả. Tùy thuộc vào trường hợp sử dụng của bạn, bạn có thể không cần sử dụng tương lai gửi trả lại. ThreadPoolExecutor nghe giống như những gì bạn đang tìm kiếm vì bạn nói rằng bạn muốn "sử dụng lại cùng một bộ chủ đề" Hy vọng rằng điều này sẽ hữu ích. Nếu không, vui lòng gửi email một ý chính với một ví dụ về mã. email của tôi là tên của tôi dấu chấm họ tại toptal dot com

ihadanny

GHI CHÚ. đối với bất kỳ ai đang cố chạy mã này ngay bây giờ [2018], api imgur đã thay đổi, nếu bạn cố truy cập https. //api. imgur. com/3/gallery/ bạn sẽ gặp lỗi `Yêu cầu không hợp lệ`. thay vào đó hãy sử dụng https. //api. imgur. com/3/gallery/ngẫu nhiên/ngẫu nhiên/

ihadanny

GHI CHÚ. đối với bất kỳ ai đang cố chạy mã này ngay bây giờ [2018], api imgur đã thay đổi, nếu bạn cố truy cập https. //api. imgur. com/3/gallery/ bạn sẽ gặp lỗi `Yêu cầu không hợp lệ`. thay vào đó hãy sử dụng https. //api. imgur. com/3/gallery/ngẫu nhiên/ngẫu nhiên/

ihadanny

API imgur đã thay đổi, hãy sử dụng https. //api. imgur. com/3/gallery/random/random/ thay vào đó

ihadanny

API imgur đã thay đổi, hãy sử dụng https. //api. imgur. com/3/gallery/random/random/ thay vào đó

Marcus McCurdy

Xin lỗi vì điều đó. Có vẻ như chúng tôi đã quên cập nhật đoạn mã đầu tiên của bài đăng trên blog. Mã thực sự chính xác trong repo Github https. //github. com/volker48/python-concurrency/blob/master/download. py#L19. Chúng tôi sẽ cập nhật đoạn trích trong bài đăng trên blog. Cảm ơn bạn đã chỉ ra vấn đề

Marcus McCurdy

Xin lỗi vì điều đó. Có vẻ như chúng tôi đã quên cập nhật đoạn mã đầu tiên của bài đăng trên blog. Mã thực sự chính xác trong repo Github https. //github. com/volker48/python-concurrency/blob/master/download. py#L19. Chúng tôi sẽ cập nhật đoạn trích trong bài đăng trên blog. Cảm ơn bạn đã chỉ ra vấn đề

Kevin Bloch

Bài viết đã được cập nhật

Kevin Bloch

Bài viết đã được cập nhật

André Zunino

Tôi đang chạy Python 3. 6. 5 và phải thay đổi lệnh gọi phương thức 'readall' trên các đối tượng HTTPResponse được trả về từ urllib. phương thức urlopen của yêu cầu [trong phần tải xuống. py]. Tôi không biết liệu chúng có bị xóa khỏi API HTTPResponse trong các phiên bản gần đây hay không nhưng tôi thấy có một phương pháp 'đọc' có thể được sử dụng

André Zunino

Tôi đang chạy Python 3. 6. 5 và phải thay đổi lệnh gọi phương thức 'readall' trên các đối tượng HTTPResponse được trả về từ urllib. phương thức urlopen của yêu cầu [trong phần tải xuống. py]. Tôi không biết liệu chúng có bị xóa khỏi API HTTPResponse trong các phiên bản gần đây hay không nhưng tôi thấy có một phương pháp 'đọc' có thể được sử dụng

Pablo Messina

Nếu tôi muốn sử dụng nhiều lõi nhưng các tác vụ cần chia sẻ cùng một dữ liệu chỉ đọc thì sao? . 1] một cơ sở dữ liệu của các giao dịch, tôi. e. một danh sách các Sự kiện mua hàng được sắp xếp [trong đó Sự kiện mua hàng là một Lớp chứa dữ liệu như dấu thời gian, ID của mặt hàng đã mua, ID của khách hàng, v.v. ] và 2] một số ma trận đối tượng cục bộ lớn có kích thước N x F [N = số mục, F = số đối tượng] và có thể 3] một số từ điển ánh xạ ID mục tới siêu dữ liệu bổ sung. Để tăng tốc quy trình, lý tưởng nhất là chạy nhiều thử nghiệm đồng thời nhưng chia sẻ dữ liệu chỉ đọc, vì sẽ không khả thi để sao chép dữ liệu cho mỗi quy trình [sẽ rất tốn thời gian và không phù hợp với . Bạn có biết làm thế nào một cái gì đó như thế này có thể được thực hiện? . Tôi đoán Đa xử lý nên là con đường để đi, nhưng sau đó tôi phải đối mặt với vấn đề chia sẻ nhiều dữ liệu chỉ đọc [danh sách các đối tượng lớp, mảng có nhiều mảng, từ điển, v.v. ]. CẬP NHẬT. Ngoài ra, bất kể giải pháp nào bạn đề xuất, liệu giải pháp đó có còn hoạt động không nếu tôi đang chạy các thử nghiệm này từ Máy tính xách tay Jupyter IPython tương tác trên Windows 10? . Tôi vừa đăng câu hỏi này rất giống nhau trên Stackoverflow. https. // stackoverflow. com/câu hỏi/51690339/run-concurrent-tasks-via-pool-of-child-processes-sharing-complex-data-memory-sh

Pablo Messina

Nếu tôi muốn sử dụng nhiều lõi nhưng các tác vụ cần chia sẻ cùng một dữ liệu chỉ đọc thì sao? . 1] một cơ sở dữ liệu của các giao dịch, tôi. e. một danh sách các Sự kiện mua hàng được sắp xếp [trong đó Sự kiện mua hàng là một Lớp chứa dữ liệu như dấu thời gian, ID của mặt hàng đã mua, ID của khách hàng, v.v. ] và 2] một số ma trận đối tượng cục bộ lớn có kích thước N x F [N = số mục, F = số đối tượng] và có thể 3] một số từ điển ánh xạ ID mục tới siêu dữ liệu bổ sung. Để tăng tốc quy trình, lý tưởng nhất là chạy nhiều thử nghiệm đồng thời nhưng chia sẻ dữ liệu chỉ đọc, vì sẽ không khả thi để sao chép dữ liệu cho mỗi quy trình [sẽ rất tốn thời gian và không phù hợp với . Bạn có biết làm thế nào một cái gì đó như thế này có thể được thực hiện? . Tôi đoán Đa xử lý nên là con đường để đi, nhưng sau đó tôi phải đối mặt với vấn đề chia sẻ nhiều dữ liệu chỉ đọc [danh sách các đối tượng lớp, mảng có nhiều mảng, từ điển, v.v. ]. CẬP NHẬT. Ngoài ra, bất kể giải pháp nào bạn đề xuất, liệu giải pháp đó có còn hoạt động không nếu tôi đang chạy các thử nghiệm này từ Máy tính xách tay Jupyter IPython tương tác trên Windows 10? . Tôi vừa đăng câu hỏi này rất giống nhau trên Stackoverflow. https. // stackoverflow. com/câu hỏi/51690339/run-concurrent-tasks-via-pool-of-child-processes-sharing-complex-data-memory-sh

Kevin Bloch

Xin chào Andre--cảm ơn bạn rất nhiều vì đã chỉ ra điều này. Phần gốc của bài viết hiện đã được cập nhật để phù hợp với các phiên bản hiện tại trên GitHub, bao gồm các bản sửa lỗi bạn đã đề cập

Kevin Bloch

Xin chào Andre--cảm ơn bạn rất nhiều vì đã chỉ ra điều này. Phần gốc của bài viết hiện đã được cập nhật để phù hợp với các phiên bản hiện tại trên GitHub, bao gồm các bản sửa lỗi bạn đã đề cập

andrei deuşteanu

Một hạn chế của thư viện đa xử lý mà tôi đã gặp là nó không thể hoạt động với các hàm lambda vì các hàm được chọn theo tên và do đó các hàm lambda là ẩn danh, chúng không được chọn. Tôi đã thử ngâm chúng thủ công với thì là nhưng không thành công. Nhưng sau đó tôi bắt gặp đa xử lý - https. //github. thư viện com/uqfoundation/multiprocess giúp mọi thứ thực sự dễ dàng chạy song song. Nó thực sự là một phần của khuôn khổ lớn hơn cho các mầm bệnh điện toán không đồng nhất - https. //github. com/uqfoundation/pathos. Bạn nên kiểm tra xem nó ra. Tôi không làm việc trên thư viện, nhưng tôi đã sử dụng nó một vài lần và nó rất dễ sử dụng, đó là lý do tại sao tôi quảng cáo nó

andrei deuşteanu

Một hạn chế của thư viện đa xử lý mà tôi đã gặp là nó không thể hoạt động với các hàm lambda vì các hàm được chọn theo tên và do đó các hàm lambda là ẩn danh, chúng không được chọn. Tôi đã thử ngâm chúng thủ công với thì là nhưng không thành công. Nhưng sau đó tôi bắt gặp đa xử lý - https. //github. thư viện com/uqfoundation/multiprocess giúp mọi thứ thực sự dễ dàng chạy song song. Nó thực sự là một phần của khuôn khổ lớn hơn cho các mầm bệnh điện toán không đồng nhất - https. //github. com/uqfoundation/pathos. Bạn nên kiểm tra xem nó ra. Tôi không làm việc trên thư viện, nhưng tôi đã sử dụng nó một vài lần và nó rất dễ sử dụng, đó là lý do tại sao tôi quảng cáo nó

Tắm Pawan

Tôi không thể hiểu làm thế nào 8 luồng được tạo trong ví dụ đầu tiên kết thúc quá trình thực thi và thoát khỏi phương thức run[]?

Tắm Pawan

Tôi không thể hiểu làm thế nào 8 luồng được tạo trong ví dụ đầu tiên kết thúc quá trình thực thi và thoát khỏi phương thức run[]?

Tắm Pawan

Và cảm ơn vì bài viết rõ ràng, súc tích và toàn diện

Tắm Pawan

Và cảm ơn vì bài viết rõ ràng, súc tích và toàn diện

Antony Raj

Bạn có thể thấy cách chúng tôi có thể sử dụng đồng thời. tương lai đây https. //Blog. franxi. tech/global-interpreter-lock-gil-and-concurrent-execution-in-python-70f17f09e892

Antony Raj

Bạn có thể thấy cách chúng tôi có thể sử dụng đồng thời. tương lai đây https. //Blog. franxi. tech/global-interpreter-lock-gil-and-concurrent-execution-in-python-70f17f09e892

jie yang

Vì 8 luồng là daemon nên khi các task trong queue làm hết thì queue. tham gia [] bỏ chặn, luồng chính kết thúc và 8 luồng daemon biến mất

jie yang

Vì 8 luồng là daemon nên khi các task trong queue làm hết thì queue. tham gia [] bỏ chặn, luồng chính kết thúc và 8 luồng daemon biến mất

Erdem Öntaş

Xin chào Marcus tôi có một câu hỏi cho bạn. Tôi có một chương trình chạy trên Raspberry Pi và có kèm theo đầu đọc thẻ. Tôi đang thu thập các Thẻ RF và gửi chúng đến máy chủ và việc đợi phản hồi của máy chủ đôi khi có thể mất tới 5 giây. Nó khiến chương trình của tôi chờ trong 5 giây này để trình đọc không hoạt động trong 5 tác phẩm này. Ngoài ra còn có một chương trình GUI. Tôi không thể tìm ra thư viện nào tôi nên sử dụng cho nhu cầu của mình. tôi nảy ra ý tưởng này. Trình đọc phải hoạt động như luồng chính và thêm THẺ RF vào hàng đợi và một luồng khác [hoặc quá trình tôi không chắc nên sử dụng cái nào] nên kiểm tra hàng đợi và gửi và chờ phản hồi trong khi trình đọc tiếp tục đọc. Bạn có gợi ý nào không?

Rêu Rêu

Xin chào Marcus - khám phá tuyệt vời về các tùy chọn khác nhau ngoài kia. Tôi đã tự hỏi lời khuyên của bạn sẽ là gì cho kịch bản này. Tôi có một ứng dụng python chạy trên AWS EC2. Nó được gọi định kỳ bởi một máy chủ web nodeJS để thực hiện một số xử lý hình ảnh chuyên sâu của CPU trên một tập hợp các hình ảnh. Tổng thời gian xử lý có thể là 20-30 giây cho khoảng 10-15 hình ảnh [trăn tuần tự tiêu chuẩn một luồng/xử lý]. Thay vào đó, để thực hiện điều này "ngay lập tức", điều tôi dự định làm là chuyển từng hình ảnh đó thành một hàm AWS lambda để tất cả chúng có thể chạy song song - một phiên bản lambda được gọi trên mỗi hình ảnh và do đó, tổng thời gian đồng hồ . Gọi các hàm lambda AWS - khi bạn muốn nhận lại phản hồi - có nghĩa là gọi chúng một cách đồng bộ. Vì vậy, để gọi tất cả chúng song song có nghĩa là hàm python cha phải tạo đồng thời. Để làm điều đó, tôi đang xem xét một trong hai a] đồng thời. tương lai, hoặc b] asyncio. Tôi đã tự hỏi suy nghĩ của bạn về những gì có thể là tốt nhất? . Richard

gia đình

Này R, bạn đã bao giờ tìm ra điều này chưa? . Tôi tin rằng tôi sắp giải quyết được vấn đề, nhưng tôi rất muốn biết bạn đã làm gì, nếu có thể. Trân trọng, J

Raphael

Tôi tin rằng, các tác vụ đa luồng của Python sử dụng cùng một lõi CPU. Đối với đa lõi, gói phải là đa xử lý và không phân luồng

Tại sao Python không phải là luồng

Python không an toàn theo luồng và ban đầu được thiết kế với một thứ gọi là GIL hoặc Khóa phiên dịch toàn cầu, đảm bảo các quy trình được thực thi tuần tự trên CPU của máy tính. On the surface, this means Python programs cannot support multiprocessing.

Luồng có làm cho Python nhanh hơn không?

Đa luồng trong Python hợp lý hóa việc sử dụng tài nguyên hiệu quả vì các luồng chia sẻ cùng một bộ nhớ và không gian dữ liệu. Nó cũng cho phép xuất hiện đồng thời nhiều tác vụ và giảm thời gian phản hồi. Điều này cải thiện hiệu suất .

Luồng trong Python có chậm không?

Kết quả kiểm tra cho thấy mã đa luồng thực sự chậm hơn đáng kể so với mã đa quy trình hoặc thậm chí thực thi tuần tự hóa . Đáng ngạc nhiên là bài kiểm tra cơ bản không có đồng thời nào vượt trội hơn tất cả các bài kiểm tra theo luồng.

Python có thể xử lý bao nhiêu luồng?

Nói chung, Python chỉ sử dụng một luồng để thực thi tập hợp các câu lệnh đã viết. Điều này có nghĩa là trong python, chỉ có một luồng sẽ được thực thi tại một thời điểm.

Chủ Đề