Hàm đa xử lý Python

Cách đơn giản nhất để sinh ra thứ hai là khởi tạo đối tượng Quy trình bằng hàm đích và gọi start() để cho phép nó bắt đầu hoạt động

import multiprocessing

def worker():
    """worker function"""
    print 'Worker'
    return

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker)
        jobs.append(p)
        p.start()

Đầu ra bao gồm từ “Công nhân” được in năm lần, mặc dù nó có thể không hoàn toàn sạch sẽ tùy thuộc vào thứ tự thực hiện

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker

Sẽ hữu ích hơn nếu có thể sinh ra một quy trình với các đối số để cho nó biết công việc cần làm. Không giống như với , để truyền đối số cho Quy trình, đối số phải có khả năng được tuần tự hóa bằng cách sử dụng. Ví dụ này chuyển cho mỗi công nhân một số để đầu ra thú vị hơn một chút

import multiprocessing

def worker(num):
    """thread worker function"""
    print 'Worker:', num
    return

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        jobs.append(p)
        p.start()

Đối số số nguyên hiện được bao gồm trong thông báo được in bởi mỗi công nhân

$ python multiprocessing_simpleargs.py

Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4

Hàm mục tiêu có thể nhập

Một điểm khác biệt giữa ví dụ và là sự bảo vệ bổ sung cho __main__ được sử dụng trong ví dụ. Do cách các quy trình mới được bắt đầu, quy trình con cần có khả năng nhập tập lệnh chứa hàm đích. Gói phần chính của ứng dụng trong một kiểm tra __main__ đảm bảo rằng nó không chạy đệ quy ở mỗi phần tử con khi mô-đun được nhập. Một cách tiếp cận khác là nhập chức năng đích từ một tập lệnh riêng

Ví dụ, chương trình chính này

import multiprocessing
import multiprocessing_import_worker

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=multiprocessing_import_worker.worker)
        jobs.append(p)
        p.start()

sử dụng hàm worker này, được xác định trong một mô-đun riêng

def worker():
    """worker function"""
    print 'Worker'
    return

và tạo đầu ra như ví dụ đầu tiên ở trên

$ python multiprocessing_import_main.py

Worker
Worker
Worker
Worker
Worker

Xác định quy trình hiện tại

Truyền đối số để xác định hoặc đặt tên cho quy trình là rườm rà và không cần thiết. Mỗi phiên bản Quy trình có một tên với giá trị mặc định có thể thay đổi khi quy trình được tạo. Quy trình đặt tên rất hữu ích để theo dõi chúng, đặc biệt là trong các ứng dụng có nhiều loại quy trình chạy đồng thời

import multiprocessing
import time

def worker():
    name = multiprocessing.current_process().name
    print name, 'Starting'
    time.sleep(2)
    print name, 'Exiting'

def my_service():
    name = multiprocessing.current_process().name
    print name, 'Starting'
    time.sleep(3)
    print name, 'Exiting'

if __name__ == '__main__':
    service = multiprocessing.Process(name='my_service', target=my_service)
    worker_1 = multiprocessing.Process(name='worker 1', target=worker)
    worker_2 = multiprocessing.Process(target=worker) # use default name

    worker_1.start()
    worker_2.start()
    service.start()

Đầu ra gỡ lỗi bao gồm tên của quy trình hiện tại trên mỗi dòng. Các dòng có Quy trình-3 trong cột tên tương ứng với quy trình chưa được đặt tên worker_1

________số 8_______

quy trình daemon

Theo mặc định, chương trình chính sẽ không thoát cho đến khi tất cả các phần tử con đã thoát. Đôi khi, việc bắt đầu một quy trình nền chạy mà không chặn chương trình chính thoát ra là rất hữu ích, chẳng hạn như trong các dịch vụ mà không có cách nào dễ dàng để làm gián đoạn nhân viên hoặc khi để nhân viên chết giữa chừng thì không.

Để đánh dấu một quá trình là daemon, hãy đặt thuộc tính daemon của nó với giá trị boolean. Giá trị mặc định dành cho các quy trình không phải là trình nền, do đó, việc chuyển True sẽ bật chế độ trình nền

import multiprocessing
import time
import sys

def daemon():
    p = multiprocessing.current_process()
    print 'Starting:', p.name, p.pid
    sys.stdout.flush()
    time.sleep(2)
    print 'Exiting :', p.name, p.pid
    sys.stdout.flush()

def non_daemon():
    p = multiprocessing.current_process()
    print 'Starting:', p.name, p.pid
    sys.stdout.flush()
    print 'Exiting :', p.name, p.pid
    sys.stdout.flush()

if __name__ == '__main__':
    d = multiprocessing.Process(name='daemon', target=daemon)
    d.daemon = True

    n = multiprocessing.Process(name='non-daemon', target=non_daemon)
    n.daemon = False

    d.start()
    time.sleep(1)
    n.start()

Đầu ra không bao gồm thông báo "Thoát" từ quy trình daemon, vì tất cả các quy trình không phải daemon (bao gồm cả chương trình chính) đều thoát trước khi quy trình daemon thức dậy sau giấc ngủ 2 giây của nó

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
0

Quá trình daemon tự động kết thúc trước khi thoát khỏi chương trình chính, để tránh để các quá trình mồ côi chạy. Bạn có thể xác minh điều này bằng cách tìm giá trị id quy trình được in khi bạn chạy chương trình, sau đó kiểm tra quy trình đó bằng một lệnh như ps

Đang chờ xử lý

Để đợi cho đến khi một quá trình hoàn thành công việc của nó và thoát, hãy sử dụng phương thức join()

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
1

Vì quá trình chính đợi daemon thoát bằng cách sử dụng tham gia(), nên thông báo “Thoát” được in lần này

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
2

Theo mặc định, khối join() vô thời hạn. Cũng có thể chuyển một đối số hết thời gian chờ (một số float biểu thị số giây để chờ quá trình không hoạt động). Nếu quá trình không hoàn thành trong khoảng thời gian chờ, thì join() vẫn trả về

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
3

Vì thời gian chờ đã trôi qua ít hơn thời gian daemon ngủ, quá trình này vẫn "sống" sau khi hàm join() trả về

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
4

Chấm dứt quá trình

Mặc dù tốt hơn là sử dụng phương pháp thuốc độc để báo hiệu cho một quy trình rằng nó nên thoát (xem phần ), nhưng nếu một quy trình có vẻ bị treo hoặc bế tắc, có thể hữu ích nếu có thể giết nó một cách cưỡng bức. Gọi term() trên một đối tượng tiến trình sẽ giết chết tiến trình con

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
5

Ghi chú

Điều quan trọng là phải tham gia () quá trình sau khi kết thúc nó để cho máy nền có thời gian cập nhật trạng thái của đối tượng để phản ánh việc kết thúc

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
6

Trạng thái thoát quy trình

Mã trạng thái được tạo khi quá trình thoát có thể được truy cập thông qua thuộc tính mã thoát

Đối với các giá trị mã thoát

  • == 0 – không có lỗi nào được tạo ra
  • > 0 – quá trình có lỗi và đã thoát với mã đó
  • < 0 – quá trình bị hủy với tín hiệu -1 * mã thoát

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
7

Các quy trình đưa ra một ngoại lệ sẽ tự động nhận được mã thoát là 1

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
8

ghi nhật ký

Khi gỡ lỗi các sự cố tương tranh, có thể hữu ích khi có quyền truy cập vào phần bên trong của các đối tượng được cung cấp bởi. Có một chức năng cấp mô-đun thuận tiện để cho phép ghi nhật ký được gọi là log_to_stderr(). Nó thiết lập đối tượng nhật ký bằng cách sử dụng và thêm trình xử lý để thông báo nhật ký được gửi đến kênh lỗi tiêu chuẩn

$ python multiprocessing_simple.py

Worker
Worker
Worker
Worker
Worker
9

Theo mặc định, mức ghi nhật ký được đặt thành NOTSET để không có thông báo nào được tạo. Vượt qua một cấp độ khác để khởi tạo trình ghi nhật ký đến mức độ chi tiết mà bạn muốn

import multiprocessing

def worker(num):
    """thread worker function"""
    print 'Worker:', num
    return

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        jobs.append(p)
        p.start()
0

Để thao tác trực tiếp với bộ ghi (thay đổi cài đặt cấp độ hoặc thêm trình xử lý), hãy sử dụng get_logger()

import multiprocessing

def worker(num):
    """thread worker function"""
    print 'Worker:', num
    return

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        jobs.append(p)
        p.start()
1

Bộ ghi nhật ký cũng có thể được định cấu hình thông qua API tệp cấu hình, sử dụng tên multiprocessing

import multiprocessing

def worker(num):
    """thread worker function"""
    print 'Worker:', num
    return

if __name__ == '__main__':
    jobs = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        jobs.append(p)
        p.start()
2

Quy trình phân lớp

Mặc dù cách đơn giản nhất để bắt đầu một công việc trong một quy trình riêng biệt là sử dụng Quy trình và chuyển một hàm đích, nhưng cũng có thể sử dụng một lớp con tùy chỉnh

Python có tốt cho đa xử lý không?

Đa xử lý Python dễ thả vào hơn phân 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.

Làm cách nào để sử dụng đa xử lý trong Python cho vòng lặp?

Điều này có thể đạt được bằng cách tạo một thể hiện Quy trình và chỉ định hàm sẽ thực thi bằng cách sử dụng đối số “đích” trong hàm tạo của lớp . Sau đó, chúng ta có thể gọi phương thức start() để bắt đầu tiến trình con mới và bắt đầu thực thi hàm đích trong tiến trình con.

Làm cách nào để sử dụng nhóm đa xử lý 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

Tôi có nên sử dụng đa luồng hoặc đa xử lý trong Python không?

Nếu chương trình của bạn bị ràng buộc bởi IO, thì cả đa luồng và đa xử lý trong Python sẽ hoạt động trơn tru . Tuy nhiên, nếu mã bị ràng buộc bởi CPU và máy của bạn có nhiều lõi, đa xử lý sẽ là lựa chọn tốt hơn.