Làm thế nào để bạn đồng bộ hóa một quá trình trong python?

Ba mươi năm trước, trong tác phẩm kinh điển The Mythical Man-Month. Tiểu luận về Kỹ thuật phần mềm (Addison-Wesley), Fred Brooks đã chỉ ra sự khác biệt giữa sự phức tạp ngẫu nhiên và sự phức tạp nội tại. Các ngôn ngữ như tiếng Anh và C ++, với các quy tắc, ngoại lệ và trường hợp đặc biệt không nhất quán của chúng, là những ví dụ về ngôn ngữ cũ. họ làm cho việc giao tiếp và lập trình trở nên khó khăn hơn mức cần thiết. Đồng thời, mặt khác, là một ví dụ điển hình của cái sau. Hầu hết mọi người phải đấu tranh để giữ một chuỗi các sự kiện trong tâm trí của họ. Theo dõi hai, ba hoặc một tá, cộng với tất cả các tương tác có thể có của chúng, thật khó

Các nhà khoa học máy tính bắt đầu nghiên cứu cách chạy nhiều quy trình một cách an toàn và hiệu quả trong một không gian địa chỉ vật lý duy nhất vào giữa những năm 1960. Kể từ đó, một lý thuyết phong phú đã được phát triển trong đó các khẳng định về hành vi của các quá trình tương tác có thể được chính thức hóa và chứng minh, và toàn bộ ngôn ngữ dành cho lập trình đồng thời và song song đã được tạo ra. Nền tảng của lập trình đa luồng, song song và phân tán, bởi Gregory R. Andrews (Addison-Wesley), không chỉ là phần giới thiệu xuất sắc về lý thuyết này, mà còn chứa đựng rất nhiều thông tin lịch sử về sự phát triển của các ý tưởng chính.

Trong 20 năm qua, cơ hội và sự cần thiết đã kết hợp lại để biến tính đồng thời trở thành một phần trong cuộc sống hàng ngày của các lập trình viên. Cơ hội dành cho tốc độ cao hơn, xuất phát từ sự sẵn có ngày càng tăng của các máy đa bộ xử lý. Vào đầu những năm 1980, đây là những sự tò mò đắt giá; . Nếu một phép tính có thể được chia thành các phần độc lập (hoặc gần như độc lập), những máy như vậy có khả năng giải chúng nhanh hơn gấp hai, bốn hoặc tám lần so với các máy đơn xử lý tương đương của chúng. Mặc dù có những giới hạn đối với lợi ích tiềm năng từ phương pháp này, nhưng nó hoạt động tốt đối với các vấn đề đa dạng như xử lý hình ảnh, phục vụ các yêu cầu HTTP và biên dịch lại nhiều tệp nguồn

Theo thuật ngữ ngày nay, các quy trình chạy trong các không gian địa chỉ logic riêng biệt được bảo vệ khỏi nhau. Sử dụng xử lý đồng thời cho các mục đích hiệu suất, đặc biệt là trong các máy đa bộ xử lý, sẽ hấp dẫn hơn với các luồng thực thi đồng thời trong cùng một chương trình, trong cùng một không gian địa chỉ mà không được bảo vệ lẫn nhau. Việc thiếu bảo vệ lẫn nhau cho phép chi phí thấp hơn và giao tiếp dễ dàng hơn và nhanh hơn, đặc biệt là do không gian địa chỉ được chia sẻ. Vì tất cả các luồng đều chạy mã từ cùng một chương trình, nên không có rủi ro bảo mật đặc biệt nào do thiếu sự bảo vệ lẫn nhau, bất kỳ rủi ro nào hơn trong chương trình đơn luồng. Do đó, đồng thời được sử dụng cho mục đích hiệu suất thường tập trung vào việc thêm các luồng vào một chương trình

Tuy nhiên, việc thêm luồng vào chương trình Python để tăng tốc chương trình thường không phải là một chiến lược thành công. Lý do cho điều này là Khóa phiên dịch toàn cầu (GIL), bảo vệ cấu trúc dữ liệu nội bộ của Python. Khóa này phải được giữ bởi một chuỗi trước khi nó có thể truy cập các đối tượng Python một cách an toàn. Không có khóa, ngay cả những thao tác đơn giản (chẳng hạn như tăng số nguyên) cũng có thể thất bại

Do đó, chỉ luồng có GIL mới có thể thao tác với các đối tượng Python hoặc gọi các hàm API Python/C. Để làm cho cuộc sống của các lập trình viên trở nên dễ dàng hơn, trình thông dịch sẽ giải phóng và yêu cầu lại khóa sau mỗi 10 lệnh mã byte (một giá trị có thể thay đổi bằng cách sử dụng sys.setcheckinterval). Khóa cũng được giải phóng và yêu cầu lại xung quanh các thao tác I/O, chẳng hạn như đọc hoặc ghi tệp, để các luồng khác có thể chạy trong khi luồng yêu cầu I/O đang đợi thao tác I/O hoàn tất. Tuy nhiên, việc khai thác tăng cường hiệu suất hiệu quả của nhiều bộ xử lý từ nhiều luồng Python thuần túy của cùng một quy trình không có trong thẻ. Trừ khi các tắc nghẽn về hiệu suất của CPU trong ứng dụng Python của bạn nằm trong các tiện ích mở rộng được mã hóa C phát hành GIL, bạn sẽ không quan sát thấy hiệu suất tăng đáng kể bằng cách chuyển ứng dụng đa luồng của mình sang máy đa bộ xử lý

Sự cần thiết của lập trình đồng thời phần lớn là do tầm quan trọng ngày càng tăng của GUI và các ứng dụng mạng. Giao diện đồ họa thường cần phải thực hiện nhiều việc cùng một lúc, chẳng hạn như hiển thị hình ảnh trong khi cuộn quảng cáo ở cuối màn hình. Mặc dù có thể thực hiện thao tác xen kẽ cần thiết theo cách thủ công, nhưng sẽ đơn giản hơn nhiều khi tự viết mã cho từng thao tác và để hệ điều hành bên dưới quyết định thứ tự cụ thể của các thao tác. Tương tự, các ứng dụng mạng thường phải nghe trên nhiều ổ cắm cùng một lúc hoặc gửi dữ liệu trên một kênh trong khi nhận dữ liệu trên một kênh khác

Hợp nhất hai loại ứng dụng này là GUI không thể biết khi nào người dùng sẽ nhấn phím hoặc di chuyển chuột và máy chủ HTTP không thể biết gói dữ liệu nào sẽ đến tiếp theo. Xử lý từng luồng sự kiện bằng một luồng điều khiển riêng biệt thường là cách đơn giản nhất để đối phó với tính không thể đoán trước này, ngay cả trên các máy có bộ xử lý đơn và khi thông lượng cao không phải là mối quan tâm hàng đầu. Lập trình hướng sự kiện cũng có thể được sử dụng trong các loại ứng dụng này và các khung Python như Medusa và asyncore là bằng chứng cho thấy cách tiếp cận này thường mang lại hiệu suất tuyệt vời với độ phức tạp, trong khi khác với đa luồng vốn có, không nhất thiết phải lớn hơn

Thư viện Python chuẩn cho phép lập trình viên tiếp cận lập trình đa luồng ở 2 cấp độ khác nhau. Mô-đun lõi, thread, là một trình bao bọc mỏng xung quanh các nguyên hàm cơ bản mà bất kỳ thư viện luồng nào cũng phải cung cấp. Ba trong số các nguyên mẫu này được sử dụng để tạo, xác định và kết thúc các luồng; . Nói chung, các lập trình viên nên tránh sử dụng trực tiếp các nguyên mẫu này và thay vào đó nên sử dụng các công cụ có trong mô-đun threading cấp cao hơn, về cơ bản thân thiện với lập trình viên hơn và có các đặc điểm hiệu suất tương tự

Các yếu tố quan trọng nhất của mô-đun threading là các lớp đại diện cho các luồng và các cấu trúc đồng bộ hóa cấp cao khác nhau. Lớp Thread đại diện cho một luồng điều khiển riêng biệt; . Một luồng có thể bắt đầu một luồng khác bằng cách gọi phương thức start của nó hoặc đợi nó hoàn thành bằng cách gọi join. Python cũng hỗ trợ các luồng daemon, xử lý nền cho đến khi tất cả các luồng không phải daemon trong chương trình thoát ra và tự động tắt

Các cấu trúc đồng bộ hóa trong mô-đun threading bao gồm các khóa, khóa vào lại (mà một luồng đơn lẻ có thể khóa lại nhiều lần một cách an toàn mà không bị bế tắc), đếm các semaphores, điều kiện và sự kiện. Các sự kiện có thể được sử dụng bởi một luồng để báo hiệu cho các luồng khác rằng có điều gì đó thú vị đã xảy ra (e. g. , rằng một mục mới đã được thêm vào hàng đợi hoặc hiện tại nó an toàn để luồng tiếp theo sửa đổi cấu trúc dữ liệu được chia sẻ). Tài liệu đi kèm với Python mô tả từng lớp này

Số lượng tương đối ít công thức nấu ăn trong chương này, so với các chương khác trong sách dạy nấu ăn này, phản ánh cả sự tập trung của Python vào năng suất của lập trình viên (chứ không phải hiệu suất tuyệt đối) và mức độ mà các gói khác (chẳng hạn như asyncore0 và wxWindows) che giấu các chi tiết khó chịu của đồng thời . Điều này cũng phản ánh xu hướng tìm kiếm cách đơn giản nhất để giải quyết bất kỳ vấn đề cụ thể nào của các lập trình viên Python, điều mà các luồng phức tạp hiếm khi gặp phải.

Tuy nhiên, sự ngắn gọn của chương này cũng có thể phản ánh sự đánh giá thấp của cộng đồng Python về tiềm năng mà luồng đơn giản có, khi được sử dụng một cách thích hợp, để đơn giản hóa cuộc sống của lập trình viên. Đặc biệt, mô-đun asyncore1 cung cấp cấu trúc hợp tác và đồng bộ hóa độc lập thú vị có thể cung cấp tất cả các dịch vụ giám sát liên luồng mà bạn cần. Hãy xem xét một chương trình điển hình, chấp nhận các yêu cầu từ GUI (hoặc từ mạng) và do các yêu cầu đó, nó thường phải đối mặt với một khối lượng công việc đáng kể có thể mất nhiều thời gian để thực hiện tất cả cùng một lúc mà chương trình

Trong một kiến ​​trúc hoàn toàn hướng sự kiện, lập trình viên có thể cần nỗ lực đáng kể để cắt đoạn công việc thành các lát công việc đủ mỏng để mỗi công việc có thể được thực hiện trong thời gian nhàn rỗi mà không bao giờ có vẻ như không phản hồi. Sau đó, chỉ một chút đa luồng có thể giúp ích đáng kể. Luồng chính đẩy phần lớn công việc nền lên một asyncore1 chuyên dụng, sau đó quay lại nhiệm vụ của nó là làm cho giao diện của chương trình luôn phản hồi nhanh

Ở đầu bên kia của asyncore1, một nhóm các chuỗi công nhân daemon đang chờ đợi, mỗi chuỗi sẵn sàng tách một yêu cầu công việc ra khỏi asyncore1 và chạy thẳng qua đó. Loại kiến ​​trúc tổng thể này kết hợp các cách tiếp cận theo hướng sự kiện và đa luồng trong lý tưởng tổng thể về sự đơn giản và do đó tối đa là Pythonic, mặc dù bạn có thể chỉ cần thêm một chút công việc nếu kết quả nỗ lực của một luồng công nhân phải được trình bày lại cho . Nếu bạn sẵn sàng gian lận một chút và sử dụng bỏ phiếu cho luồng chính chủ yếu hướng sự kiện để truy cập kết quả asyncore1 trở lại từ luồng worker daemonic, hãy xem Công thức 9. 7 để biết công việc nhỏ đó có thể đơn giản như thế nào

Làm cách nào để sử dụng nhóm trong Python?

Nhóm đa xử lý Python. Hướng dẫn đầy đủ .
Tạo nhóm quy trình
Gửi nhiệm vụ đến Nhóm quy trình
Đợi nhiệm vụ hoàn thành (Tùy chọn)
Tắt nhóm quy trình

Hàng đợi đa xử lý của Python hoạt động như thế nào?

Hàng đợi là cấu trúc dữ liệu mà các mục có thể được thêm vào bằng lệnh gọi put() và từ đó có thể truy xuất các mục bằng lệnh gọi get() . đa xử lý. Hàng đợi cung cấp hàng đợi FIFO nhập trước, xuất trước, có nghĩa là các mục được truy xuất từ ​​hàng đợi theo thứ tự chúng được thêm vào.

Khóa đa xử lý trong Python là gì?

Python cung cấp khóa loại trừ lẫn nhau để sử dụng với các quy trình thông qua đa xử lý. Khóa lớp . Một phiên bản của khóa có thể được tạo và sau đó được các quy trình thu thập trước khi truy cập phần quan trọng và được giải phóng sau phần quan trọng.

Khi nào sử dụng Python đa xử lý?

Nhóm đa xử lý Python có thể được sử dụng để thực thi song song một hàm trên nhiều giá trị đầu vào , phân phối dữ liệu đầu vào trên các quy trình (song song hóa dữ liệu). Dưới đây là một ví dụ về Nhóm đa xử lý Python đơn giản.

Chủ đề