Đối với tôi, điều này thực sự khá đơn giản:
Các tiến trình con tùy chọn:
subprocess
là để chạy các tệp thực thi khác --- về cơ bản nó là một trình bao bọc xung quanh os.fork()
và os.execve()
với một số hỗ trợ cho hệ thống ống nước tùy chọn (thiết lập PIPE đến và đi từ các quy trình con. Rõ ràng là bạn có thể có các cơ chế truyền thông liên quá trình (IPC) khác, chẳng hạn như ổ cắm hoặc Posix hoặc Bộ nhớ chia sẻ SysV. Nhưng bạn sẽ bị giới hạn ở bất kỳ giao diện nào và các kênh IPC được hỗ trợ bởi các chương trình bạn đang gọi.
Thông thường, một người sử dụng bất kỳ subprocess
đồng bộ nào --- chỉ cần gọi một số tiện ích bên ngoài và đọc lại kết quả đầu ra của nó hoặc chờ hoàn thành (có thể đọc kết quả của nó từ một tệp tạm thời hoặc sau khi nó được đăng chúng lên cơ sở dữ liệu nào đó).
Tuy nhiên, người ta có thể sinh ra hàng trăm quy trình con và thăm dò chúng. Classh tiện ích yêu thích cá nhân của riêng tôi thực hiện chính xác điều đó.
Nhược điểm lớn nhất của subprocess
mô-đun là hỗ trợ I / O thường bị chặn. Có một bản dự thảo PEP-3145 để sửa lỗi đó trong một số phiên bản tương lai của Python 3.x và một asyncproc thay thế (Cảnh báo dẫn đến quyền tải xuống, không phải bất kỳ loại tài liệu nào cũng như README). Tôi cũng nhận thấy rằng việc nhập fcntl
và thao tác Popen
trực tiếp các bộ mô tả tệp PIPE của bạn tương đối dễ dàng --- mặc dù tôi không biết liệu điều này có khả dụng với các nền tảng không phải UNIX hay không.
(Cập nhật: ngày 7 tháng 8 năm 2019: Hỗ trợ Python 3 cho các quy trình con ayncio : asyncio Subprocessses )
subprocess
hầu như không có hỗ trợ xử lý sự kiện ... mặc dù bạn có thể sử dụng signal
mô-đun và các tín hiệu UNIX / Linux kiểu cũ đơn giản --- giết chết các quy trình của bạn một cách nhẹ nhàng, như ban đầu.
Các đa xử tùy chọn:
multiprocessing
là để chạy các chức năng trong mã (Python) hiện có của bạn với sự hỗ trợ cho các giao tiếp linh hoạt hơn giữa các quy trình này. Đặc biệt, tốt nhất là bạn nên xây dựng multiprocessing
IPC của mình xung quanh các Queue
đối tượng của mô-đun nếu có thể, nhưng bạn cũng có thể sử dụng Event
các đối tượng và nhiều tính năng khác (một số trong số đó, có lẽ, được xây dựng xung quanh mmap
hỗ trợ trên các nền tảng mà sự hỗ trợ đó là đủ).
multiprocessing
Mô-đun của Python nhằm cung cấp các giao diện và tính năng rất giống threading
trong khi cho phép CPython mở rộng quy mô xử lý của bạn giữa nhiều CPU / lõi bất chấp GIL (Global Interpreter Lock). Nó tận dụng tất cả nỗ lực khóa và đồng tiền hóa SMP chi tiết đã được thực hiện bởi các nhà phát triển nhân hệ điều hành của bạn.
Các luồng tùy chọn:
threading
là cho một phạm vi tương đối hẹp của các ứng dụng đó là I / O bị ràng buộc (không cần phải quy mô trên nhiều lõi CPU) và đó được hưởng lợi từ độ trễ cực thấp và chuyển đổi chi phí của chuyển đổi chủ đề (với bộ nhớ lõi được chia sẻ) quá trình vs. / chuyển đổi ngữ cảnh. Trên Linux, đây gần như là tập hợp trống (thời gian chuyển đổi quy trình Linux rất gần với chuyển mạch luồng của nó).
threading
mắc phải hai nhược điểm lớn trong Python .
Tất nhiên, một trong những cách triển khai cụ thể --- chủ yếu ảnh hưởng đến CPython. Đó là GIL. Phần lớn, hầu hết các chương trình CPython sẽ không được hưởng lợi từ sự sẵn có của hơn hai CPU (lõi) và thường thì hiệu suất sẽ bị ảnh hưởng bởi tranh chấp khóa GIL.
Vấn đề lớn hơn không phải là việc triển khai cụ thể, là các luồng chia sẻ cùng một bộ nhớ, bộ xử lý tín hiệu, bộ mô tả tệp và một số tài nguyên hệ điều hành khác. Do đó, lập trình viên phải cực kỳ cẩn thận về việc khóa đối tượng, xử lý ngoại lệ và các khía cạnh khác của mã của họ, những thứ vừa tinh vi vừa có thể giết chết, đình trệ hoặc bế tắc toàn bộ quá trình (bộ luồng).
Bằng cách so sánh, multiprocessing
mô hình cung cấp cho mỗi quy trình bộ nhớ riêng, bộ mô tả tệp, v.v. Một sự cố hoặc ngoại lệ không được xử lý trong bất kỳ một trong số chúng sẽ chỉ giết tài nguyên đó và xử lý mạnh mẽ sự biến mất của quy trình con hoặc anh chị em có thể dễ dàng hơn đáng kể so với gỡ lỗi, cô lập và khắc phục hoặc giải quyết các vấn đề tương tự trong chuỗi.
- (Lưu ý: việc sử dụng
threading
với các hệ thống Python chính, chẳng hạn như NumPy , có thể ít bị tranh chấp GIL hơn đáng kể so với hầu hết mã Python của riêng bạn. Đó là bởi vì chúng đã được thiết kế đặc biệt để làm như vậy; các phần gốc / nhị phân của NumPy, ví dụ: sẽ giải phóng GIL khi điều đó an toàn).
Các xoắn tùy chọn:
Cũng cần lưu ý rằng Twisted cung cấp một giải pháp thay thế khác vừa thanh lịch nhưng rất khó hiểu . Về cơ bản, trước nguy cơ đơn giản hóa quá mức đến mức người hâm mộ Twisted có thể xông vào nhà tôi với những cây ném và ngọn đuốc, Twisted cung cấp tính năng đa tác vụ hợp tác theo hướng sự kiện trong bất kỳ quy trình (đơn lẻ) nào.
Để hiểu làm thế nào điều này có thể xảy ra, người ta nên đọc về các tính năng của select()
(có thể được xây dựng xung quanh lệnh select () hoặc thăm dò () hoặc các lệnh gọi hệ điều hành tương tự). Về cơ bản, tất cả đều được thúc đẩy bởi khả năng yêu cầu HĐH ở chế độ ngủ chờ bất kỳ hoạt động nào trên danh sách các bộ mô tả tệp hoặc một số thời gian chờ.
Đánh thức từ mỗi lệnh gọi tới select()
là một sự kiện --- hoặc một sự kiện liên quan đến đầu vào có sẵn (có thể đọc được) trên một số ổ cắm hoặc bộ mô tả tệp hoặc không gian đệm trở nên khả dụng trên một số bộ mô tả hoặc ổ cắm (có thể ghi) khác, một số điều kiện ngoại lệ (TCP chẳng hạn như gói PUSH'd ngoài băng) hoặc TIMEOUT.
Do đó, mô hình lập trình Twisted được xây dựng xung quanh việc xử lý các sự kiện này sau đó lặp lại trên trình xử lý "chính" kết quả, cho phép nó gửi các sự kiện đến trình xử lý của bạn.
Cá nhân tôi nghĩ về cái tên, Twisted như gợi liên tưởng đến mô hình lập trình ... vì cách tiếp cận vấn đề của bạn, theo một nghĩa nào đó, phải "xoắn" từ trong ra ngoài. Thay vì quan niệm chương trình của bạn như một chuỗi hoạt động trên dữ liệu đầu vào và đầu ra hoặc kết quả, bạn đang viết chương trình của mình dưới dạng dịch vụ hoặc daemon và xác định cách nó phản ứng với các sự kiện khác nhau. (Trên thực tế, "vòng lặp chính" cốt lõi của chương trình Twisted là (thường là? Luôn luôn?) A reactor()
).
Những thách thức lớn đối với việc sử dụng Twisted bao gồm việc xoay chuyển tâm trí của bạn xung quanh mô hình điều khiển sự kiện và cũng tránh sử dụng bất kỳ thư viện lớp hoặc bộ công cụ nào không được viết để hợp tác trong khuôn khổ Twisted. Đây là lý do tại sao Twisted cung cấp các mô-đun của riêng mình để xử lý giao thức SSH, cho các lời nguyền, và các chức năng của quy trình con / Popen của riêng nó, và nhiều mô-đun và trình xử lý giao thức khác, thoạt đầu, dường như sẽ sao chép mọi thứ trong các thư viện chuẩn Python.
Tôi nghĩ sẽ rất hữu ích nếu hiểu về Twisted ở mức độ khái niệm ngay cả khi bạn không bao giờ có ý định sử dụng nó. Nó có thể cung cấp thông tin chi tiết về hiệu suất, tranh chấp và xử lý sự kiện trong quá trình xử lý luồng, xử lý đa quy trình và thậm chí cả quy trình phụ của bạn cũng như bất kỳ xử lý phân tán nào mà bạn thực hiện.
( Lưu ý: Các phiên bản mới hơn của Python 3.x bao gồm các tính năng asyncio (I / O không đồng bộ) như async def , trình trang trí @ async.coroutine và từ khóa await , và mang lại lợi nhuận từ hỗ trợ trong tương lai . Tất cả những điều này gần giống với Xoắn từ góc độ quy trình (đa nhiệm hợp tác)). (Để biết trạng thái hiện tại của hỗ trợ Twisted cho Python 3, hãy xem: https://twistedmatrix.com/documents/current/core/howto/python3.html )
Các phân phối tùy chọn:
Tuy nhiên, một lĩnh vực xử lý khác mà bạn chưa từng hỏi đến, nhưng điều đáng xem xét, đó là xử lý phân tán . Có nhiều công cụ và khuôn khổ Python để xử lý phân tán và tính toán song song. Cá nhân tôi nghĩ rằng cái dễ sử dụng nhất là cái ít được coi là có trong không gian đó.
Việc xây dựng quá trình xử lý phân tán xung quanh Redis gần như là chuyện nhỏ . Toàn bộ kho lưu trữ khóa có thể được sử dụng để lưu trữ các đơn vị công việc và kết quả, DANH SÁCH Redis có thể được sử dụng Queue()
như đối tượng tương tự và hỗ trợ PUB / SUB có thể được sử dụng để Event
xử lý giống như vậy. Bạn có thể băm các khóa của mình và sử dụng các giá trị, được sao chép qua một cụm bản sao Redis lỏng lẻo, để lưu trữ cấu trúc liên kết và ánh xạ mã thông báo băm để cung cấp hàm băm nhất quán và dự phòng để mở rộng vượt quá khả năng của bất kỳ bản sao đơn lẻ nào để điều phối các nhân viên của bạn và sắp xếp dữ liệu (pickled, JSON, BSON hoặc YAML) trong số đó.
Tất nhiên khi bạn bắt đầu xây dựng một giải pháp quy mô lớn hơn và phức tạp hơn xung quanh Redis, bạn đang triển khai lại nhiều tính năng đã được giải quyết bằng cách sử dụng Celery , Apache Spark và Hadoop , Zookeeper , etcd , Cassandra , v.v. Tất cả chúng đều có các mô-đun để truy cập Python vào các dịch vụ của họ.
[Cập nhật: Một số tài nguyên cần xem xét nếu bạn đang xem xét Python để tính toán chuyên sâu trên các hệ thống phân tán: IPython Parallel và PySpark . Mặc dù đây là những hệ thống máy tính phân tán có mục đích chung, nhưng chúng là những hệ thống phân tích và khoa học dữ liệu đặc biệt dễ tiếp cận và phổ biến].
Phần kết luận
Ở đó bạn có hàng loạt các lựa chọn xử lý thay thế cho Python, từ đơn luồng, với các lệnh gọi đồng bộ đơn giản đến các quy trình con, nhóm các quy trình con được thăm dò ý kiến, xử lý đa luồng và đa xử lý, đa tác vụ hợp tác theo hướng sự kiện và ra ngoài để xử lý phân tán.