Python có thể xử lý bao nhiêu chủ đề?

Luồng là một cách tiếp cận nổi tiếng khác để đạt được sự đồng thời. 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 thường có trọng lượng nhẹ hơn các quy trình và chúng có yêu cầu bộ nhớ thấp hơn nhiều vì chúng chia sẻ cùng một không gian bộ nhớ

Trong bài học này, chúng tôi sẽ sử dụng phân luồng Python để tăng hiệu suất của trình tải xuống hình ảnh của chúng tôi. Để làm điều này, chúng tôi sẽ tạo một nhóm gồm 8 luồng, tạo thành tổng cộng 9 luồng bao gồm cả luồng chính. Số luồng tối ưu thường được chọn dựa trên các yếu tố như các ứng dụng và dịch vụ khác đang chạy trên cùng một máy. Chúng tôi sẽ chọn 8 luồng công nhân vì nhiều máy tính cá nhân có 8 lõi CPU và một luồng công nhân trên mỗi lõi có vẻ như là một con số tốt cho số lượng luồng chạy cùng một lúc

Phiên bản luồng của chương trình hầu như giống như simple.py với ngoại lệ là bây giờ chúng ta có một lớp mới, DownloadWorker, kế thừa từ lớp Thread. Lớp này cung cấp một phương thức run nên được ghi đè bằng một phương thức thực hiện công việc thực tế của luồng

Chủ đề an toàn

Như đã đề cập trước đó, mỗi luồng chia sẻ cùng một không gian bộ nhớ. Nghĩa là, các biến trong chương trình được chia sẻ bởi tất cả các luồng và không thể truy cập theo cách bạn thường truy cập vào một biến. Điều này là do các luồng đang thực thi đồng thời và một luồng có thể thay đổi biến trong khi luồng khác đang đọc nó hoặc tệ hơn là hai luồng có thể cố gắng cập nhật biến cùng một lúc. Điều này được gọi là điều kiện chạy đua và là một trong những nguồn lỗi hàng đầu trong các chương trình luồng. Thay vào đó, cần sử dụng các lớp đặc biệt cho phép nhiều luồng truy cập chúng đồng thời. Chúng được gọi là các lớp an toàn luồng

Trong trường hợp của chúng tôi, chúng tôi sẽ cung cấp cho luồng một phương thức run tải xuống hình ảnh trong một vòng lặp vô hạn. Chúng tôi sẽ sử dụng cấu trúc dữ liệu an toàn cho chuỗi được gọi là Queue để theo dõi các URL mà mỗi chuỗi sẽ tải xuống. Trên mỗi lần lặp lại, luồng sẽ gọi self.queue.get() để thử và tìm nạp một URL để tải xuống. Cuộc gọi này tạm dừng luồng cho đến khi có một mục trong hàng đợi để luồng công nhân xử lý. Khi worker nhận được một mục từ hàng đợi, nó sẽ gọi phương thức download_link 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ì Queue theo dõi có bao nhiêu tác vụ được xử lý. Cuộc gọi đến Thread1 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ụ

from time import time
from queue import Queue
from threading import Thread

from download import setup_download_dir, get_links, download_link

CLIENT_ID = 'replace with your client ID'

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()
           download_link(directory, link)
           self.queue.task_done()

def main():
   ts = time()
   download_dir = setup_download_dir()
   links = [l for l in 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:
       print('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()
   print('Took {}'.format(time() - ts))

if __name__ == '__main__':
   main()

Thách đấu

Tạo tệp Thread2 bằng chương trình được cung cấp ở trên. Thay thế chuỗi Thread3 trong Thread2 bằng ID khách hàng mà bạn lấy được từ Imgur. Chạy Thread2 và xác minh rằng bạn có được một số hình ảnh trong thư mục Thread6. So sánh mất bao lâu để tải xuống những hình ảnh này với phiên bản simple.pyThread8

Khóa phiên dịch toàn cầu (GIL)

Trong đa luồng, khóa là cơ chế ngăn nhiều luồng truy cập đồng thời vào cùng một biến được chia sẻ. Điều này là để tránh một điều kiện cuộc đua, như đã đề cập trước đây. CPython sử dụng khóa trình thông dịch toàn cầu, còn được gọi là GIL, để ngăn chặn việc thực thi nhiều luồng cùng một lúc trong trình thông dịch Python. Điều này ngăn hai luồng vô tình làm hỏng cấu trúc dữ liệu Python bên trong. Mặc dù nghe có vẻ như điều này sẽ ngăn nhiều luồng hữu ích trong Python, nhưng trên thực tế vẫn có nhiều thao tác vẫn có thể được thực hiện song song. Một điều đặc biệt là thực hiện các thao tác nhập/xuất, chẳng hạn như truy cập một trang web

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ụ: Jython, một triển khai Java của Python và IronPython, một triển khai Python sử dụng. NET framework đều không có GIL. Bạn có thể tìm thấy danh sách triển khai Python đang hoạt động

Chạy tập lệnh 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à 3. 1 giây. Đó là nhanh gấp 3 lần so với ví dụ simple.py

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ụ ràng buộc đầu vào/đầu ra. 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 phân luồng 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

Nếu chương trình đang thực hiện một tác vụ bị ràng buộc bởi CPU, thì 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. Đối với các tác vụ ràng buộc CPU và thực thi song song thực sự trong Python, mô-đun simple.py0 là một tùy chọn tốt hơn

Python có thể chạy nhiều luồng không?

Tóm lại, phân luồng trong Python cho phép tạo nhiều luồng trong một quy trình duy nhất , nhưng do GIL, sẽ không có luồng nào trong số chúng . Phân luồng vẫn là một tùy chọn rất tốt khi chạy đồng thời nhiều tác vụ ràng buộc I/O.

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

Python là một ngôn ngữ tuyến tính. Tuy nhiên, mô-đun phân luồng hữu ích khi bạn cần thêm sức mạnh xử lý . Lưu ý rằng mặc dù đa luồng trong Python là lựa chọn hoàn hảo cho các tác vụ và thao tác I/O, nhưng nó không thể được sử dụng cho các quy trình quét web vì bộ xử lý đang đợi dữ liệu và không hoạt động.

Có giới hạn bao nhiêu chủ đề không?

4. 2. các cửa sổ. Trên máy Windows, không có giới hạn nào được chỉ định cho luồng . Do đó, chúng tôi có thể tạo bao nhiêu luồng tùy thích, cho đến khi hệ thống của chúng tôi hết bộ nhớ hệ thống khả dụng.

Các luồng Python có thể chạy trên nhiều lõi không?

Thư viện luồng. Ở trên, chúng tôi đã đề cập đến thực tế là Python trên trình thông dịch CPython không hỗ trợ thực thi đa lõi thực sự thông qua đa luồng. Tuy nhiên, Python KHÔNG có thư viện Threading