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
0import 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[]
1import 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[]
2import 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[]
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ạiAPI 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ườngimport 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 thiMộ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 RQimport 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
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[]
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 PythonTrong 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 độngSử 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ênChạ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ầnKhô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 aiohttpHã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[]
7Cú 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 HTTPHà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ốngNê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ẻ
PythonConcurrencyParallelismThreadingMultiprocessingNgườ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