Có bao nhiêu quy trình nên chạy đa xử lý Python?

Sau nhiều yêu cầu, một số kế hoạch và dành thời gian để cung cấp một bản tóm tắt thực tế — tôi rất vui được chia sẻ một hướng dẫn cho phép bạn bắt đầu sử dụng xử lý song song trong mã Python của mình

TL; DR

P xử lý song song có thể tăng tốc mã của bạn và xử lý nhiều công việc để thực thi trên mỗi mẫu tập dữ liệu. Theo mô hình này, Đa xử lý trong Python cho phép bạn chạy đồng thời nhiều quy trình. Chúng tôi thảo luận về bốn thành phần quan trọng của gói Đa xử lý trong Python. Quy trình, Khóa, Hàng đợi và Nhóm [Hình 1].

Mục lục ________ 0 Giới thiệu

D ata thường được giao nhiệm vụ xử lý một tập hợp các điểm dữ liệu bằng các kỹ thuật chuyển đổi khác nhau. Nói cách khác, với một tập dữ liệu, chúng tôi đặt mục tiêu xử lý trước dữ liệu thông qua lọc, chuyển đổi, làm sạch, chia tỷ lệ, v.v. Ngoài ra, dữ liệu kết quả thường phải được xử lý hậu kỳ để có độ nổi bật và dễ hiểu hoặc để phân tích và trực quan hóa kết quả. Do đó, cùng một công việc phải được thực hiện N lần cho N mẫu dữ liệu — như các nhà khoa học dữ liệu có kinh nghiệm có thể tranh luận, các bước trước và sau khi lập mô hình đóng vai trò là nút thắt cổ chai bổ sung. Bạn muốn tìm hiểu cách giảm thiểu chi phí thời gian bằng cách tận dụng toàn bộ sức mạnh của CPU?

Hãy để chúng tôi tìm hiểu cách tăng tốc thời gian chạy của chúng tôi trong Python bằng một vài dòng mã bổ sung. Nhưng, trước tiên, chúng ta hãy chạy tập hợp các quy trình song song. Tất cả kiến ​​thức bạn cần để bắt đầu bao gồm bốn thành phần của gói Đa xử lý — Quy trình, Khóa, Hàng đợi và Nhóm [Hình 1]

Chúng tôi bắt đầu bằng cách xác định đa xử lý đồng thời nhấn mạnh trường hợp sử dụng của nó. Sau đây, chúng tôi thảo luận về đa xử lý dành riêng cho lập trình Python. Sau đó, bốn thành phần cần thiết để bắt đầu được mô tả và minh họa thông qua các định nghĩa, mã mẫu và hình ảnh; . Cuối cùng, khi kết thúc, chúng tôi xem xét các tài nguyên bổ sung cho những người muốn đưa việc học lên cấp độ tiếp theo. Mã hoàn chỉnh có trên Github

Đa xử lý là gì và tại sao lại sử dụng nó?

Đa xử lý đề cập đến việc chạy nhiều quy trình đồng thời, điều này có thể cực kỳ hữu ích để tăng tốc mã của bạn và xử lý các tập dữ liệu và tác vụ lớn. Ví dụ: chạy song song một hoạt động có thể chia công việc thành nhiều phần nhỏ hơn để có thể xử lý đồng thời. Thực thi bộ xử lý đơn điển hình, chi phí thời gian là N x M, trong đó M là thời gian cho mỗi quá trình đơn [i. e. , đơn vị thời gian], giảm xuống còn [N/C] x M, trong đó C là số lõi CPU. Xem xét N có thể phát triển cực kỳ lớn trong mô hình dữ liệu lớn thời hiện đại, chúng ta có thể giảm thời gian. Điều này có thể đặc biệt hữu ích khi máy tính của bạn có nhiều lõi hoặc bộ xử lý. Ngoài ra, đa xử lý có thể cải thiện hiệu suất của các tác vụ sử dụng nhiều I/O bằng cách trải rộng chúng trên các quy trình khác nhau [Hình 2]

Hình 2. Một mô tả của các loại xử lý. Tài nguyên được truy cập trong một tác vụ [hộp văn bản màu xanh lam] truy cập tài nguyên bộ nhớ [màu xanh lục] để xử lý tác vụ trên luồng lõi CPU [màu đỏ]. Như đã đề cập, các tác vụ sử dụng nhiều CPU vốn có khả năng song song hóa có thể chạy trên nhiều bộ xử lý [phải], đây là chủ đề của blog. Đa luồng xứng đáng có một blog riêng — mong đợi phần tiếp theo về chủ đề này. Xem tài liệu bổ sung ở cuối để biết tài nguyên về bộ nhớ và tài nguyên. Tác giả đã tạo hình minh họa

Mặc dù đa xử lý có thể cực kỳ hữu ích, nhưng điều quan trọng cần lưu ý là một số nguy cơ tiềm ẩn liên quan đến việc sử dụng nó. Ví dụ: nếu hai quy trình cố gắng truy cập đồng thời vào cùng một tài nguyên, điều này có thể dẫn đến hỏng dữ liệu hoặc không nhất quán. Do đó, khóa là điều cần thiết để bảo vệ tài nguyên được chia sẻ giữa các quy trình. Đồng thời, một lập trình viên hiệu quả sẽ hiểu được khả năng của phần cứng và từ đó tận dụng tối đa tiềm năng của nó [Hình 3]

Hình 3. Ảnh GIF cho thấy sự khác biệt giữa xử lý đơn lõi [trên cùng] và xử lý đa lõi [i. e. , Đa xử lý, phía dưới]. Hình minh họa mô tả cùng một CPU với bốn lõi được bao quanh bởi mô tả bốn tác vụ [i. e. , T1, T2, …, T4]. Lưu ý cách lõi đơn xử lý các tác vụ một cách tuần tự, phải hoàn thành nhiệm vụ đầu tiên, sau đó là nhiệm vụ tiếp theo, v.v. Mặt khác, Đa xử lý tận dụng cả bốn lõi để xử lý các chương trình [còn gọi là công việc, nhiệm vụ, v.v. ] song song. Tác giả đã tạo hoạt hình

Đối với những người học trực quan tốt nhất, hãy để chúng tôi mô tả sự so sánh giữa đơn xử lý và đa xử lý theo cách khác. như một chức năng của bộ xử lý và công việc [Hình 4]

hinh 4. Rõ ràng là so sánh mô hình xử lý đơn lẻ với mô hình đa xử lý, M công việc [i. e. , 4, cho ví dụ này] được thực hiện nối tiếp nhau [trên cùng] hoặc đồng thời như các quá trình độc lập. Cái nào có vẻ hiệu quả hơn? . Đa xử lý trong Python

Đa xử lý tận dụng nhiều lõi CPU để tăng tốc tải máy tính. Python sử dụng Khóa phiên dịch toàn cầu [i. e. , GIL], một mô hình để tránh rò rỉ bộ nhớ và các điều kiện chạy đua không mong muốn thông qua sơ đồ đếm tham chiếu được triển khai để quản lý bộ nhớ. Trong trường hợp GIL, giống như hầu hết các chiến lược triển khai, có ưu và nhược điểm, chúng ta hãy xem xét một lỗ hổng trung tâm. nó hạn chế mã byte Python chạy trên một luồng từ cùng một lõi. Bên cạnh đó, GIL là đối tượng của một blog trong tương lai. Hiện tại, hãy biết rằng trong Python cốt lõi, lập trình song song bị vô hiệu hóa một cách có chủ ý

Python cung cấp một số công cụ để triển khai đa xử lý thông qua gói có tên đó, bao gồm Quy trình, Khóa, Hàng đợi và Nhóm. Chúng ta sẽ thảo luận về từng thành phần này và đưa ra ví dụ về cách chúng có thể được sử dụng tiếp theo. Để bắt đầu, chúng ta hãy nhập gói và xác định số lượng lõi có sẵn trên hệ thống [Hình 5]

Hình 5. Nhập và gọi 'cpu_count[]' để biết có bao nhiêu lõi trên CPU hiện tại. Tác giả đã tạo hình minh họa. Sử dụng thư viện đa xử lý — Process, Lock, Queue và Pool

Mỗi trong số bốn thành phần thiết yếu của đa xử lý của Python phục vụ một mục đích cụ thể. Dưới đây là tổng quan ngắn gọn về từng người

1. Quá trình

Đây là đơn vị thực thi cơ bản trong Python. Mọi quy trình đều có bản sao trình thông dịch Python và không gian bộ nhớ của nó, cho phép nhiều công việc được thực thi đồng thời mà không có xung đột

Lớp Process được xử lý như sau. [1] quy trình hiện tại được sao chép qua rẽ nhánh;

Cụ thể, và như ví dụ sau cho thấy, Quy trình được quản lý thông qua hai chức năng,

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
9 và
import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
0

Ví dụ và học tập thực hành

Trước tiên, chúng ta hãy định nghĩa một hàm đơn giản là ngủ cho

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
1 với giá trị mặc định được đặt thành nửa giây

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

Tiếp theo, hãy tạo nhiều quy trình bằng gói Đa xử lý

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]

Đối số

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
2 cho
import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
3 chỉ định chức năng đích mà quy trình chạy. Nhưng các quy trình này không chạy ngay lập tức cho đến khi bắt đầu

________số 8

Đặt tất cả lại với nhau, chúng ta có những điều sau đây

import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]

Câu lệnh in sẽ được đặt làm dấu ngoặc kép như sau

Chương trình kết thúc trong 0. 013 giây
Ngủ trong 0. 5 giây
Ngủ trong 0. 5 giây
Ngủ xong
Ngủ xong

Lưu ý rằng, mặc dù hai cuộc gọi đến

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
4 đã được thực hiện, người ta sẽ mong đợi rằng chương trình sẽ mất ít nhất 1 giây [i. e. , ngủ hai lần trong nửa giây]. Hơn nữa, câu lệnh in ở cuối chương trình xuất hiện trước những câu lệnh được thực thi trước đó thông qua lệnh gọi hàm. Tại sao lại thế này? . Như đã đề cập ở trên,
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
9 và
import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
0 là các phương thức mà lớp
import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
3 sử dụng cùng nhau. Nơi chúng tôi đã làm
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
9, chúng tôi không bao giờ
import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
0. Hãy để chúng tôi kiểm tra luồng chương trình bằng sơ đồ [Hình 6]

Hình 6. Khi `join[]` không được gọi, chương trình sẽ thực thi tiến trình chính song song với các tiến trình khác. Do đó, thời gian in là cần thiết để in ở phần cuối của sơ đồ chính do tác giả tạo

Trước khi sửa lỗi này, chúng ta hãy nhấn mạnh một lưu ý quan trọng về đa xử lý trong Python

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
6

Bây giờ, quay lại chương trình của chúng ta dường như đã hết thứ tự, không phải vậy, nhưng nó không được đặt để chờ. Chúng ta cần gọi hàm

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
0 trên hai quy trình để làm cho chúng chạy trước khi in thời gian. Điều này là do ba quá trình đang diễn ra.
p1.start[]
p2.start[]
1,
p1.start[]
p2.start[]
2, và quy trình chính. Quy trình chính là quy trình theo dõi và in thời gian thực hiện. Chúng ta nên làm cho dòng
p1.start[]
p2.start[]
3 chạy không sớm hơn quá trình
p1.start[]
p2.start[]
1 và
p1.start[]
p2.start[]
2 kết thúc. Chúng ta chỉ cần thêm đoạn mã này ngay sau khi gọi hàm
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
9

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
4

Phương thức

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
0 buộc các quy trình khác phải đợi cho đến khi các quy trình gọi phương thức này hoàn tất. Đây là đầu ra với các câu lệnh nối được thêm vào

Ngủ trong 0. 5 giây
Ngủ trong 0. 5 giây
Đã ngủ xong
Đã ngủ xong
Chương trình đã hoàn tất sau 0. 5688213340181392 giây

Hình 7. Bây giờ, chương trình sẽ đợi cho đến khi các công việc song song hoàn tất, vì `join[]` được gọi cho mỗi phiên bản quy trình. Sơ đồ được tạo bởi tác giả

Hãy cho chúng tôi xem luồng chương trình được cập nhật [Hình 7]

Với lý luận tương tự, chúng ta có thể làm cho nhiều quy trình chạy hơn. Sau đây là mã hoàn chỉnh được sửa đổi từ trên để có 10 quy trình

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
6

Ngủ cho 1 người. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ 1 giấc. 0 giây
Ngủ xong
Ngủ xong
Ngủ xong
Ngủ xong
Finished sleeping
Finished sleeping
Finished sleeping
Finished sleeping
Finished sleeping
Finished sleeping
Program finished in 1.019 seconds

Quá trình kết thúc với mã thoát 0

Nếu chúng ta chạy đoạn mã trên, nó gọi

p1.start[]
p2.start[]
8 mười lần. không xử lý đa lõi, chúng tôi dự kiến ​​mã sẽ mất ít nhất mười giây

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
8

2. Khóa

Như đã đề cập, một quy trình là một thể hiện đang chạy của một chương trình, đóng gói mọi chương trình Python [i. e. , mỗi cái là một phiên bản mới của trình thông dịch Python].

p1.start[]
p2.start[]
9 là tên quy trình khi
import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]
0 chạy dưới dạng tập lệnh — một khía cạnh mà chúng tôi đã xem xét ở trên thông qua sơ đồ. Ngoài ra, chúng tôi đã học được rằng các quy trình con có thể được sinh ra và chạy đồng thời thông qua một phiên bản _____91. Thông thường, các chương trình đồng thời này chia sẻ dữ liệu hoặc tài nguyên giữa các quy trình. Khóa loại trừ lẫn nhau bảo vệ các tài nguyên được chia sẻ và ngăn chặn các điều kiện cạnh tranh

Cơ chế được sử dụng phổ biến nhất để đảm bảo loại trừ lẫn nhau là khóa loại trừ lẫn nhau hoặc mutex hoặc đơn giản là khóa. Mutex là một loại đối tượng đặc biệt có hỗ trợ trong phần cứng cơ bản. Ý tưởng cơ bản là mỗi phần quan trọng được bảo vệ bằng khóa

— TRANG 53, GIỚI THIỆU VỀ LẬP TRÌNH SONG SONG, 2020

Khóa được sử dụng để bảo vệ tài nguyên được chia sẻ giữa các quy trình. Chúng cho phép nhiều công việc truy cập vào một tài nguyên mà không có xung đột. Ngoài ra, Khóa rất cần thiết để đảm bảo rằng dữ liệu nhất quán giữa các công việc

Khóa cho phép mã khẳng định chắc chắn rằng không quy trình nào khác có thể thực thi mã tương tự cho đến khi khóa được giải phóng. Vì vậy, mục đích của lớp Lock là hai lần. [1] để yêu cầu khóa thông qua chức năng

import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]
2;

Hãy để chúng tôi xây dựng trên bài học của

import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]
1 ở trên. Đây là mã hoàn chỉnh để chạy

import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
5

Đầu ra như sau

>tiến trình 0 bị khóa, ngủ trong 0. 297
>tiến trình 1 có khóa, ngủ trong 0. 908
>tiến trình 2 bị khóa, ngủ trong 0. 223
>tiến trình 3 có khóa, ngủ trong 0. 016
>tiến trình 4 có khóa, ngủ trong 0. 323
>tiến trình 5 có khóa, ngủ trong 0. 796
>tiến trình 6 bị khóa, ngủ trong 0. 664
>tiến trình 7 bị khóa, ngủ trong 0. 337
>tiến trình 8 bị khóa, ngủ trong 0. 068
>tiến trình 9 có khóa, ngủ trong 0. 116

Quá trình kết thúc với mã thoát 0

Điều gì sẽ xảy ra nếu chúng ta chạy lại cùng một đoạn mã

>tiến trình 0 bị khóa, ngủ trong 0. 223
>tiến trình 1 có khóa, ngủ trong 0. 175
>tiến trình 2 có khóa, đang ngủ trong 0. 148
>tiến trình 3 có khóa, ngủ trong 0. 773
>tiến trình 4 bị khóa, ngủ trong 0. 180
>tiến trình 5 có khóa, ngủ trong 0. 294
>tiến trình 7 bị khóa, ngủ trong 0. 864
>tiến trình 6 bị khóa, ngủ trong 0. 687
>tiến trình 8 bị khóa, ngủ trong 0. 813
>tiến trình 9 bị khóa, ngủ trong 0. 735

Quá trình kết thúc với mã thoát 0

Lưu ý trong lần chạy thứ hai rằng ID5 đi trước ID7 và quay lại ID6. Làm sao chuyện này có thể?

Chúng tôi có thể phát triển một ví dụ để minh họa cách sử dụng khóa mutex bằng cách chia đoạn mã mẫu ở trên thành nhiều phần

Đầu tiên, chúng ta có thể định nghĩa một hàm tác vụ đích lấy khóa làm đối số và sử dụng khóa để bảo vệ phần quan trọng

Phần quan trọng liên quan đến việc báo cáo một tin nhắn và chặn trong một phần giây

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
0

Tiếp theo, chúng ta có thể tạo một phiên bản của

import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]
5 cho các quy trình để chia sẻ

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
1

Sau đó, chúng tôi có thể tạo nhiều quy trình được định cấu hình để thực thi hàm task[] của mình và cạnh tranh để thực thi phần quan trọng

Mỗi quy trình sẽ nhận được khóa dùng chung, ID số nguyên từ 0 đến 9 và thời gian ngủ ngẫu nhiên tính bằng giây trong khoảng từ 0 đến 1 làm đầu vào

Như đã hoàn thành đối với mã hoàn chỉnh trong phần trước, chúng ta có thể triển khai điều này thông qua hiểu danh sách, tạo danh sách mười phiên bản

import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]
1 được định cấu hình

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
2

Tiếp theo, chúng ta có thể bắt đầu tất cả các quy trình

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
3

Cuối cùng, chúng ta có thể đợi tất cả các tiến trình con mới kết thúc

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
4

Liên kết điều này lại với nhau, ví dụ hoàn chỉnh về việc sử dụng khóa được liệt kê bên dưới

Mã bắt đầu với mười quy trình được định cấu hình để thực thi chức năng tùy chỉnh của chúng tôi

Sau đó, các tiến trình con được bắt đầu và tiến trình chính sẽ chặn cho đến khi tất cả các tiến trình con kết thúc

Mỗi tiến trình con cố gắng lấy khóa trong hàm

import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]
7. Mỗi lần chỉ có một phương pháp có thể lấy được khóa và sau khi thực hiện xong, chúng sẽ báo cáo một thông báo bao gồm id của chúng và thời gian chúng sẽ ngủ. Sau đó, quá trình này sẽ chặn trong một phần giây trước khi nhả khóa

3. Xếp hàng

Cho phép các tiến trình giao tiếp với nhau. Ví dụ: dữ liệu có thể được đặt trên hàng đợi và được xử lý bởi bộ xử lý khác khi có sẵn, cho phép chúng tôi chia tác vụ thành các phần nhỏ hơn để có thể xử lý đồng thời

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
5

đẩy các mục vào hàng đợi.
mục số. 1 màu đỏ
số mặt hàng. 2 màu xanh
số mặt hàng. 3 màu xanh
số mặt hàng. 4 màu đen

xuất hiện các mục từ hàng đợi.
mục số. 0 đỏ
số mặt hàng. 1 mặt hàng
màu xanh lục. 2 màu xanh
số mặt hàng. 3 màu đen

4. Hồ bơi

Nhóm là tập hợp các quy trình được sử dụng để thực hiện các tác vụ song song. Nhóm giúp chia một nhiệm vụ to lớn thành các phần nhỏ hơn mà nhiều bộ xử lý có thể xử lý

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
6

đầu ra

Quy trình A chờ 5 giây
Quy trình B chờ 2 giây
Quy trình C chờ 1 giây
Quy trình D chờ .
Process C Finished.
Quy trình B đã hoàn tất.
Quy trình D đã hoàn tất.
Quá trình A đã hoàn tất.

Lưu ý rằng bây giờ chúng ta có thể đặt số lượng công nhân để thực hiện song song

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
7

Với bộ

import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]
8 sẽ có
import multiprocessing
import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']

if __name__ == "__main__":
start_time = time.perf_counter[]

# Creates two processes
p1 = multiprocessing.Process[target=sleep]
p2 = multiprocessing.Process[target=sleep]

# Starts both processes
p1.start[]
p2.start[]

finish_time = time.perf_counter[]

print[f"Program finished in {[finish_time - start_time]:.3f} seconds"]
9 và danh sách có 4 phần tử, nghĩa là mỗi phần tử được thực thi đồng thời. Do đó, hãy lưu ý câu lệnh in hiển thị thứ tự của mỗi lần hoàn thiện là thứ tự giống nhau tính bằng giây được đặt thành
import multiprocessing
p1 = multiprocessing.Process[target=task]
p2 = multiprocessing.Process[target=task]
4

Mẹo về cách bắt đầu với đa xử lý

Nếu bạn chưa quen với đa xử lý trong Python, đây là một số mẹo giúp bạn bắt đầu

  1. Làm quen với những kiến ​​thức cơ bản về quản lý quy trình Python. Tài liệu dành cho mô-đun Quy trình có thể giúp bạn hiểu cách bắt đầu và kiểm soát các quy trình
  2. Hãy chắc chắn rằng bạn biết những điều cơ bản về khóa. Mô-đun khóa cung cấp tất cả các công cụ bạn cần để bảo vệ tài nguyên được chia sẻ giữa các quy trình
  3. Sử dụng hàng đợi để giao tiếp giữa các quy trình giúp việc truyền dữ liệu qua lại giữa các công việc trở nên dễ dàng
  4. Cuộc thí nghiệm. Cách tốt nhất để học cách sử dụng đa xử lý là thử nghiệm các kỹ thuật khác nhau và xem cách nào phù hợp nhất với nhu cầu của bạn
Mẹo về các phương pháp hay nhất để sử dụng đa xử lý

Khi sử dụng đa xử lý trong các dự án của bạn, hãy nhớ một số phương pháp hay nhất. Dưới đây là một số mẹo giúp bạn tận dụng tối đa tính năng đa xử lý

1. Sử dụng các tác vụ sử dụng nhiều CPU. Không phải tất cả các công việc đều phù hợp để chạy song song. Tuy nhiên, các công việc sử dụng nhiều CPU là lý tưởng cho đa xử lý vì chúng có thể chia thành các phần nhỏ hơn và chạy đồng thời

2. Hạn chế sử dụng tài nguyên chia sẻ. Như đã đề cập trước đó, hai quy trình cố gắng truy cập cùng một tài nguyên có thể dẫn đến hỏng hoặc không nhất quán dữ liệu. Điều cần thiết là hạn chế sử dụng tài nguyên được chia sẻ giữa các quy trình để tránh mọi xung đột tiềm ẩn

3. Sử dụng ổ khóa cẩn thận. Khóa rất cần thiết để bảo vệ tài nguyên được chia sẻ giữa các quy trình, nhưng điều quan trọng là phải sử dụng chúng cẩn thận. Bạn có thể dễ dàng tạo bế tắc hoặc các sự cố đồng bộ hóa khác nếu không cẩn thận

4. Sử dụng hàng đợi để liên lạc. Hàng đợi cho phép các quy trình giao tiếp với nhau, điều này có thể hữu ích cho các tác vụ yêu cầu sự phối hợp giữa nhiều tác vụ

5. Kiểm tra kỹ mã của bạn. Luôn luôn là một ý tưởng hay để kiểm tra nó trước khi triển khai nó trong môi trường sản xuất. Đa xử lý có thể gây ra các sự cố mới và không lường trước được, do đó, điều cần thiết là phải phát hiện ra bất kỳ sự cố tiềm ẩn nào trước khi chúng gây ra sự cố trong ứng dụng của bạn

Sự kết luận

Đa xử lý trong Python có thể là một công cụ mạnh mẽ để tăng tốc mã của bạn. Bằng cách phân chia tác vụ giữa nhiều bộ xử lý, bạn thường có thể đạt được kết quả nhanh hơn so với khi bạn chạy cùng một tác vụ trên một bộ xử lý. Trong bài viết này, chúng ta đã xem xét bốn thành phần đa xử lý thiết yếu của Python. Chúng tôi cũng đã thảo luận về một số phương pháp hay nhất để sử dụng đa xử lý một cách hiệu quả

Trong tương lai, chúng ta sẽ khám phá các trường hợp sử dụng đa xử lý trong Python, chẳng hạn như truyền thông điệp và khái niệm bộ nhớ dùng chung. Do đó, chúng tôi sẽ quay lại

import time

def sleep[sleep_sec=0.5]:
print[f'Sleeping for {sleep_sec} seconds']
time.sleep[sleep_sec]
print['Finished sleeping']
61 và giới thiệu các cấu trúc dữ liệu khác cho phép triển khai điều này tương đối nhanh chóng

Tôi nên chạy Python bao nhiêu quy trình?

Có lẽ bạn nên đặt số lượng quy trình là bằng với số lượng lõi CPU logic trong hệ thống của mình , e. g. mặc định. Theo mặc định. Đặt thành số lõi CPU hợp lý.

Có bao nhiêu quy trình có thể có được một phiên bản khóa đa xử lý đồng thời?

Mỗi lần chỉ có một quy trình có thể lấy được khóa và sau khi thực hiện xong, chúng sẽ báo cáo một thông báo bao gồm id của mình và thời gian chúng sẽ ngủ.

Đa xử lý trong Python có tăng tốc không?

Nó được sử dụng để tăng tốc đáng kể cho chương trình của bạn, đặc biệt nếu chương trình có nhiều tác vụ đòi hỏi nhiều CPU . Trong trường hợp này, nhiều chức năng có thể chạy cùng nhau vì mỗi chức năng sẽ sử dụng một lõi CPU khác nhau, điều này sẽ cải thiện việc sử dụng CPU.

Đa xử lý Python có sử dụng nhiều lõi không?

Các quy trình Python thường sử dụng một luồng đơn vì GIL. Mặc dù có 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 triển khai dựa trên C hoàn toàn, cho phép sử dụng nhiều lõi .

Chủ Đề