Python chạy trong luồng riêng biệt

Trong bài viết này, chúng tôi thảo luận về ý nghĩa thực tế của một tác vụ chạy không đồng bộ so với trong một chuỗi riêng biệt so với trong một quy trình riêng biệt. Tôi cũng sẽ cố gắng hết sức để minh họa cách thức hoạt động của những thứ này, sự khác biệt và cạm bẫy để bạn hy vọng tránh được những sai lầm tương tự mà tôi đã mắc phải với những thứ này khi tôi triển khai chúng vào các dự án của mình

Trước tiên, chúng tôi xác định ý nghĩa của việc chạy mã đồng bộ, không đồng bộ [asyncio], đồng thời [phân luồng] và song song [đa xử lý], ít nhất là từ quan điểm của Python. Sau đó, chúng tôi xem xét thời điểm sử dụng cái nào và các thách thức liên quan đến từng tùy chọn đa nhiệm

Thực thi đồng bộ

Thực sự không có gì đặc biệt trong việc thực hiện đồng bộ. Nó chỉ có nghĩa là chúng tôi chạy mã như bình thường. một điều xảy ra và sau đó một điều khác xảy ra. Chỉ có một chức năng có thể chạy tại một thời điểm và chỉ khi nó kết thúc, một chức năng khác mới được phép xảy ra

Đây là một ví dụ đơn giản

Bản in này như người ta mong đợi

Running do_first
Running do_second

Không có gì quá thú vị và có lẽ bạn đã quá quen thuộc. Tiếp theo chúng ta sẽ xem sự khác biệt với mã không đồng bộ là gì

Thực thi không đồng bộ [async]

Trong chế độ không đồng bộ, chúng tôi chạy một khối mã tại một thời điểm nhưng chúng tôi xoay vòng xem khối mã nào đang chạy. Chương trình của bạn cần được xây dựng xung quanh tính năng không đồng bộ mặc dù bạn có thể gọi các hàm [đồng bộ] thông thường từ chương trình không đồng bộ

Dưới đây là danh sách những gì bạn cần để làm cho chương trình của bạn không đồng bộ

  • Thêm từ khóa async trước các khai báo chức năng của bạn để làm cho chúng có thể chờ được
  • Thêm từ khóa chờ khi bạn gọi các chức năng không đồng bộ của mình [không có nó thì chúng sẽ không chạy]
  • Tạo tác vụ từ các chức năng không đồng bộ mà bạn muốn bắt đầu không đồng bộ. Cũng chờ kết thúc của họ
  • Gọi không đồng bộ. chạy để bắt đầu phần không đồng bộ của chương trình của bạn

Dưới đây là một ví dụ về một chương trình không đồng bộ đơn giản chạy hai chức năng không đồng bộ

kết quả này

Running do_first block 1
Running do_second block 1
Running do_first block 2
Running do_second block 2

Như chúng ta có thể thấy, chúng ta chạy khối mã đầu tiên trong chức năng đầu tiên, sau đó chúng tôi trả lại phần thực thi cho công cụ async, sau đó chạy khối mã đầu tiên trong chức năng thứ hai [trong khi chức năng đầu tiên chưa hoàn thành], sau đó chúng tôi

Chúng tôi đã chạy đồng thời các khối để tạo ấn tượng về việc thực thi song song

Thực thi đồng thời [luồng]

Trong luồng, chúng tôi thực thi một dòng mã tại một thời điểm nhưng chúng tôi liên tục thay đổi dòng nào được chạy. Điều này được thực hiện bằng thư viện luồng. trước tiên chúng tôi tạo một số chủ đề, bắt đầu chúng và sau đó đợi chúng kết thúc [ví dụ: sử dụng tham gia]

Đây là một ví dụ về một chương trình như vậy

Điều này xuất ra cho tôi

Running do_first line 1
Running do_second line 1
Running do_first line 2
Running do_second line 2
Running do_second line 3
Running do_first line 3

Như chúng ta có thể thấy, nó được xoay rõ ràng giữa các dòng mà nó thực thi từ mỗi chức năng này. Nó chạy các dòng mã từ các chức năng này hơi ngẫu nhiên

Lưu ý rằng các dòng mã không được chạy cùng một lúc. Đây chưa phải là sự song song thực sự

Thực thi song song [đa xử lý]

Trong đa xử lý, chúng tôi thực sự chạy nhiều dòng mã Python cùng một lúc. Chúng tôi sử dụng nhiều quy trình để đạt được điều này. Để sử dụng đa xử lý, bạn cần phải. tạo các quy trình, đặt chúng chạy và đợi chúng kết thúc [ví dụ: sử dụng tham gia]

Đây là một ví dụ

Đối với tôi, điều này thực sự đã chạy chức năng đầu tiên và sau đó là chức năng thứ hai nhưng điều này là do việc khởi chạy một quy trình rất tốn kém và quy trình đầu tiên kết thúc khi bắt đầu quy trình thứ hai. Nếu đây là các chức năng chạy lâu hơn, chúng ta sẽ thấy rằng các chức năng được thực thi song song và thực sự cùng lúc nếu chúng ta có các lõi miễn phí

Sự khác biệt lớn

Bây giờ chúng ta đã biết các tùy chọn, chúng ta thảo luận một chút về những khác biệt chính

  • Đồng bộ so với những người khác. Trong thực thi đồng bộ, chúng ta có thể quyết định mọi thứ sẽ chạy theo thứ tự nào. Trong trường hợp không đồng bộ, phân luồng và đa xử lý, chúng tôi để hệ thống bên dưới quyết định
  • Đa xử lý so với những người khác. Đa xử lý là thứ duy nhất thực sự chạy nhiều dòng mã cùng một lúc. Không đồng bộ và sắp xếp luồng giả mạo nó. Tuy nhiên, async và threading có thể chạy nhiều hoạt động IO thực sự cùng một lúc
  • Asyncio vs luồng. Async chạy một khối mã tại một thời điểm trong khi chỉ phân luồng một dòng mã tại một thời điểm. Với async, chúng tôi kiểm soát tốt hơn khi thực thi được cấp cho khối mã khác nhưng chúng tôi phải tự giải phóng thực thi

Cũng lưu ý rằng do GIL [Khóa phiên dịch toàn cầu], chỉ đa xử lý mới thực sự được song song hóa

Vậy khi nào thì sử dụng cái nào?

Bây giờ chúng ta đã biết ý nghĩa của từng ý nghĩa và sự khác biệt của chúng, chúng ta sẽ thảo luận khi nào nên sử dụng cái này hơn cái kia. Có lẽ cách đơn giản nhất để so sánh đây là các vấn đề về giới hạn IO so với giới hạn CPU

  • vấn đề ràng buộc IO. sử dụng async nếu thư viện của bạn hỗ trợ nó và nếu không, hãy sử dụng luồng
  • Vấn đề ràng buộc CPU. sử dụng đa xử lý
  • Không có gì ở trên là một vấn đề. bạn có thể ổn với mã đồng bộ. Bạn vẫn có thể muốn sử dụng async để có cảm giác phản hồi trong trường hợp mã của bạn tương tác với người dùng

Ngoài ra, nếu mã của bạn có khả năng bị kẹt [ví dụ: vòng lặp vô hạn], đa xử lý là cách duy nhất có thể được kết thúc một cách đáng tin cậy. Bạn thực sự không thể chấm dứt các tác vụ theo luồng và với async, nếu nó bị kẹt và không bao giờ gọi chờ, thì async cũng không thể chấm dứt nó. Tuy nhiên, nếu tác vụ bị mắc kẹt đang chờ, nó có thể bị chấm dứt

Cá nhân tôi thích async hơn khi có thể vì các điều kiện chủng tộc khó quản lý hơn với đa xử lý và phân luồng thường gây ra các sự cố khó gỡ lỗi. Điều kiện cuộc đua là trường hợp thứ tự thực hiện phụ thuộc vào thời gian của hệ điều hành. Ví dụ: đôi khi một trong các luồng của bạn có thể chạy nhanh hơn các luồng khác do cơ hội thuần túy có thể thay đổi hành vi của chương trình của bạn. Điều này có thể dẫn đến các lỗi khó tái tạo và hiếm khi xảy ra

Dưới đây là tóm tắt các lựa chọn

cạm bẫy

Có một số hạn chế trong mỗi tùy chọn có thể gây ra những khó khăn không mong muốn hoặc hoàn toàn khiến bạn không thể sử dụng tùy chọn đó. Tiếp theo chúng ta sẽ thảo luận một chút về những cạm bẫy này để bạn chuẩn bị tốt hơn

Không đồng bộ

Async có vấn đề là mã của bạn cần được xây dựng cùng với nó và cả thư viện của bạn để hỗ trợ nó. Ví dụ: nếu bạn có một truy vấn cơ sở dữ liệu nhưng máy khách cơ sở dữ liệu của bạn không hỗ trợ async, điều đó có nghĩa là bạn không thể chạy nhiều truy vấn cùng lúc, điều này sẽ làm mất đi lợi ích của mã đồng bộ

Ngoài ra, bạn không nên kết hợp async với giẫm đạp. Async không phải là chủ đề an toàn

xâu chuỗi

Luồng có vấn đề là nếu nhiều luồng hoạt động trên cùng một dữ liệu, sẽ có nguy cơ bị hỏng hoặc thậm chí bị sập. Ví dụ: nếu một luồng đóng kết nối mà một luồng khác hiện đang sử dụng, thì có một vấn đề rõ ràng. Các loại sự cố này hiếm gặp hơn với async vì mỗi khối mã sẽ chỉ trả lại quá trình thực thi khi an toàn

Bạn cần đảm bảo rằng các chức năng là luồng an toàn nếu chúng chia sẻ dữ liệu hoặc đối tượng. Sử dụng khóa luồng nếu cần

đa xử lý

Nghe có vẻ đa xử lý là tốt nhất cho mọi thứ dựa trên những gì chúng ta đã thảo luận cho đến nay. Tuy nhiên, có nhiều vấn đề nghiêm trọng xảy ra với đa xử lý

  • Nó khá tốn kém để tạo ra các quy trình
  • Bạn không thể chia sẻ bộ nhớ giữa các quy trình [mặc dù bạn có thể truyền dữ liệu qua đường ống]
  • Việc tuần tự hóa dữ liệu cho các quy trình không phải lúc nào cũng đơn giản

Đặc biệt là cái cuối cùng thường không được thảo luận nhiều nhưng khi gặp phải, nó gây ra những lỗi kỳ lạ thường gây nhầm lẫn để giải quyết. Ví dụ: trình trang trí có thể bị lỗi trên Windows với PermissionError

nhưng trên Linux, chúng thường hoạt động tốt. Ngoài ra, việc chuyển các chức năng được trang trí sang Quy trình dưới dạng đối số không thành công trong việc tẩy. Tôi cũng đã gặp phải các lỗi kỳ lạ khác trong các phiên bản Python mới hơn nhưng không phải trong các phiên bản cũ hơn

Thông thường, tôi tránh sử dụng các công cụ trang trí với đa xử lý và tôi cố gắng loại bỏ các thuộc tính mà tôi không cần trong quy trình đối với các lớp tôi đã tạo và sử dụng trong đa xử lý

Trong khối mã ở trên, chúng tôi đã ghi đè __getstate__ xử lý việc chọn cá thể để xóa một thuộc tính mà chúng tôi không thể chọn [không thể chọn bộ đệm tệp]

Cũng lưu ý rằng các tiến trình con chạy lại quá trình nhập, vì vậy bạn phải luôn sử dụng if __name__ == “__main__”. khối để bắt đầu chương trình của bạn. Bạn cũng nên tránh xử lý nặng khi nhập và khai báo toàn cục, hàm và lớp

lời kết thúc

Tôi gặp khá nhiều vấn đề khi triển khai những điều này cho back-end tự động hóa của mình và đã có lúc tôi cảm thấy khá tuyệt vọng khi cố gắng giải quyết một số lỗi kỳ lạ do điều kiện cuộc đua và lỗi chọn lọc. Có lẽ bài viết này giúp bạn tránh được những sai lầm tương tự và hướng dẫn bạn chọn đúng dạng tương tranh hoặc song song cho bài toán của mình. Tôi hy vọng bạn tìm thấy bài viết hữu ích ít nhất

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

Tóm lại, phân luồng trong Python cho phép nhiều luồng được tạo trong một quy trình duy nhất , nhưng do GIL, sẽ không có luồng nào trong số đó . 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.

Hàm Python nào có thể được gọi để chạy một luồng?

Trong Python, chúng ta có thể tạo và chạy các luồng bằng mô-đun threading

Tại sao Python không tốt cho đa luồng?

Python không hỗ trợ đa luồng vì 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 luồng. GIL không ngăn luồng.

Python có chạy trên một luồng không?

Python KHÔNG phải là ngôn ngữ đơn luồng . Các quy trình Python thường sử dụng một luồng đơn vì GIL. Bất chấp GIL, các thư viện thực hiện các tác vụ tính toán nặng như numpy, scipy và pytorch sử dụng các triển khai dựa trên C dưới mui xe, cho phép sử dụng nhiều lõi.

Chủ Đề