Python có hỗ trợ đa luồng không? Nó có thể tăng tốc thời gian thực hiện không?


95

Tôi hơi bối rối về việc liệu đa luồng có hoạt động trong Python hay không.

Tôi biết đã có rất nhiều câu hỏi về vấn đề này và tôi đã đọc nhiều câu hỏi trong số đó, nhưng tôi vẫn bối rối. Tôi biết từ kinh nghiệm của chính mình và đã thấy những người khác đăng câu trả lời và ví dụ của riêng họ ở đây trên StackOverflow rằng đa luồng thực sự có thể thực hiện được trong Python. Vậy tại sao mọi người cứ nói rằng Python bị GIL khóa và chỉ có một luồng có thể chạy tại một thời điểm? Nó rõ ràng hoạt động. Hay có sự khác biệt nào đó mà tôi không hiểu ở đây?

Nhiều người đăng / người trả lời cũng đề cập đến việc phân luồng bị hạn chế vì nó không tận dụng được nhiều lõi. Nhưng tôi muốn nói rằng chúng vẫn hữu ích vì chúng hoạt động đồng thời và do đó hoàn thành khối lượng công việc tổng hợp nhanh hơn. Ý tôi là tại sao thậm chí sẽ có một mô-đun chuỗi Python nếu không?

Cập nhật:

Cảm ơn cho tất cả các câu trả lời cho đến nay. Theo cách hiểu của tôi là đa luồng sẽ chỉ chạy song song đối với một số tác vụ IO, nhưng chỉ có thể chạy từng tác vụ một đối với nhiều tác vụ lõi có giới hạn CPU.

Tôi không hoàn toàn chắc chắn điều này có ý nghĩa gì đối với tôi về mặt thực tế, vì vậy tôi sẽ chỉ đưa ra một ví dụ về loại nhiệm vụ mà tôi muốn đa luồng. Ví dụ: giả sử tôi muốn lặp qua một danh sách rất dài các chuỗi và tôi muốn thực hiện một số thao tác chuỗi cơ bản trên mỗi mục danh sách. Nếu tôi chia nhỏ danh sách, gửi từng danh sách con để được xử lý bởi mã vòng lặp / chuỗi của tôi trong một chuỗi mới và gửi lại kết quả trong một hàng đợi, liệu các khối lượng công việc này có chạy gần như cùng một lúc không? Quan trọng nhất về mặt lý thuyết, điều này có tăng tốc thời gian chạy script không?

Một ví dụ khác có thể là nếu tôi có thể kết xuất và lưu bốn hình ảnh khác nhau bằng cách sử dụng PIL trong bốn luồng khác nhau và điều này có nhanh hơn xử lý từng hình ảnh một không? Tôi đoán thành phần tốc độ này là điều tôi thực sự băn khoăn hơn là thuật ngữ chính xác là gì.

Tôi cũng biết về mô-đun đa xử lý nhưng mối quan tâm chính của tôi bây giờ là tải tác vụ từ nhỏ đến trung bình (10-30 giây) và vì vậy tôi nghĩ đa luồng sẽ thích hợp hơn vì các quá trình con có thể khởi động chậm.


4
Đây là một câu hỏi khá tải. Tôi nghĩ rằng câu trả lời nằm ở những gì bạn muốn có các chủ đề làm. Trong hầu hết các trường hợp, GIL ngăn nhiều hơn 1 luồng chạy đồng thời. Tuy nhiên, có một số trường hợp GIL được giải phóng (ví dụ: đọc từ một tệp) để có thể thực hiện song song. Cũng lưu ý rằng GIL là một chi tiết triển khai của Cpython (cách triển khai phổ biến nhất). Không thực hiện khác của python (Jython, PyPy, vv) có một GIL (AFAIK)
mgilson

2
@mgilson PyPy có GIL.

2
@delnan - Có vẻ như bạn đã đúng. Cảm ơn.
mgilson

1
"các quy trình con có thể khởi động chậm" - bạn có thể tạo một nhóm các tác vụ sẵn sàng để thực thi. Chi phí có thể được giới hạn trong khoảng thời gian cần thiết để tuần tự hóa / giải mã hóa dữ liệu cần thiết để tác vụ bắt đầu hoạt động.
Brian Cain

1
@KarimBahgat, đó chính là ý tôi.
Brian Cain

Câu trả lời:


132

GIL không ngăn chặn luồng. Tất cả những gì GIL làm là đảm bảo chỉ một luồng đang thực thi mã Python tại một thời điểm; điều khiển vẫn chuyển đổi giữa các luồng.

Điều mà GIL ngăn cản sau đó, là sử dụng nhiều hơn một lõi CPU hoặc các CPU riêng biệt để chạy các luồng song song.

Điều này chỉ áp dụng cho mã Python. Các phần mở rộng C có thể và thực sự phát hành GIL để cho phép nhiều luồng mã C và một luồng Python chạy trên nhiều lõi. Điều này mở rộng đến I / O được kiểm soát bởi hạt nhân, chẳng hạn như select()các lệnh gọi đọc và ghi socket, làm cho Python xử lý các sự kiện mạng một cách hiệu quả một cách hợp lý trong một thiết lập đa lõi đa luồng.

Những gì nhiều triển khai máy chủ sau đó làm, là chạy nhiều hơn một quy trình Python, để cho phép Hệ điều hành xử lý việc lập lịch giữa các quy trình để sử dụng tối đa lõi CPU của bạn. Bạn cũng có thể sử dụng multiprocessingthư viện để xử lý quá trình xử lý song song trên nhiều quy trình từ một cơ sở mã và quy trình mẹ, nếu điều đó phù hợp với trường hợp sử dụng của bạn.

Lưu ý rằng GIL chỉ áp dụng cho việc triển khai CPython; Jython và IronPython sử dụng một triển khai luồng khác nhau (tương ứng là các luồng thời gian chạy chung của Java VM và .NET).

Để giải quyết trực tiếp bản cập nhật của bạn: Bất kỳ tác vụ nào cố gắng tăng tốc độ từ thực thi song song, sử dụng mã Python thuần túy, sẽ không thấy tốc độ tăng vì mã Python theo luồng bị khóa với một luồng thực thi tại một thời điểm. Tuy nhiên, nếu bạn kết hợp các phần mở rộng C và I / O (chẳng hạn như PIL hoặc hoạt động numpy) và bất kỳ mã C nào có thể chạy song song với một luồng Python đang hoạt động.

Phân luồng Python rất tốt để tạo GUI đáp ứng hoặc để xử lý nhiều yêu cầu web ngắn trong đó I / O là nút thắt cổ chai nhiều hơn so với mã Python. Nó không phù hợp để song song hóa mã Python chuyên sâu về tính toán, gắn vào multiprocessingmô-đun cho các tác vụ như vậy hoặc ủy quyền cho một thư viện bên ngoài chuyên dụng.


Cảm ơn @MartijnPieters, sau đó tôi có câu trả lời rõ ràng hơn cho câu hỏi của tôi về việc liệu phân luồng có thể được sử dụng để tăng tốc mã như vòng lặp for, là "không". Có thể bạn hoặc ai đó có thể viết một câu trả lời mới mà tôi có thể chấp nhận, cung cấp một số ví dụ cụ thể về các mô-đun / mã / hoạt động phổ biến trong đó luồng sẽ được GIL cho phép chạy paralell và do đó nhanh hơn (ví dụ: ví dụ về các I / O và mạng / các hoạt động đọc socket đã được đề cập và bất kỳ trường hợp nào khác mà đa luồng trong Python là hữu ích). Có thể là một danh sách hay về cách sử dụng đa luồng phổ biến và một số ví dụ lập trình nếu có?
Karim Bahgat

4
Không, tôi không nghĩ rằng một câu trả lời như vậy sẽ rất hữu ích; một cách trung thực. Bạn không thể tạo một danh sách đầy đủ, nhưng nguyên tắc chung là mọi I / O (đọc và ghi tệp, ổ cắm mạng, đường ống) đều được xử lý trong C và nhiều thư viện C cũng phát hành GIL cho nhưng tùy thuộc vào các thư viện để ghi lại điều này cho bạn.
Martijn Pieters

1
Thật tệ, tôi không thấy câu trả lời cập nhật của bạn cho đến bây giờ, nơi bạn đã đưa ra một số ví dụ hay về cách sử dụng chuỗi. Chúng bao gồm (sửa cho tôi nếu tôi sai) lập trình mạng (ví dụ urllib.urlopen()?), Để gọi một tập lệnh Python từ bên trong một GUI Python và gọi nhiều hoạt động PIL (ví dụ Image.transform()) và numpy (ví dụ numpy.array()) với các luồng. Và bạn đã cung cấp thêm một số ví dụ trong nhận xét của mình chẳng hạn như sử dụng nhiều luồng để đọc tệp (ví dụ f.read()?). Tôi biết là không thể có một danh sách đầy đủ, chỉ muốn các loại ví dụ bạn đưa ra trong bản cập nhật của mình. Dù bằng cách nào, chấp nhận câu trả lời của bạn :)
Karim Bahgat

2
@KarimBahgat: Vâng, urllib.urlopen()sẽ gọi các ổ cắm mạng, chờ I / O ổ cắm là một cơ hội tuyệt vời để chuyển luồng và làm việc khác.
Martijn Pieters

4
Mặc dù nó không liên quan trực tiếp đến vấn đề này, nhưng cần lưu ý rằng đôi khi phân luồng không liên quan đến hiệu suất; nó có thể đơn giản hơn khi viết mã của bạn dưới dạng nhiều chuỗi thực thi độc lập. Ví dụ: bạn có thể có một luồng đang phát nhạc nền, một luồng phục vụ giao diện người dùng và một luồng xử lý các tính toán phải được thực hiện cuối cùng nhưng không vội vàng. Cố gắng trình tự phát bộ đệm âm thanh tiếp theo với giao diện người dùng runloop hoặc chia nhỏ tính toán của bạn thành các phần đủ nhỏ để không ảnh hưởng đến tương tác, có thể khó hơn nhiều so với sử dụng các chuỗi.
abarnert

4

Đúng. :)

Bạn có mô-đun luồng cấp thấp và mô-đun luồng cấp cao hơn . Nhưng nếu bạn chỉ muốn sử dụng các máy đa lõi, thì mô-đun đa xử lý là con đường để đi.

Trích dẫn từ tài liệu :

Trong CPython, do Khóa thông dịch toàn cầu, chỉ một luồng có thể thực thi mã Python cùng một lúc (mặc dù một số thư viện hướng hiệu suất nhất định có thể khắc phục hạn chế này). Nếu bạn muốn ứng dụng của mình sử dụng tốt hơn các tài nguyên tính toán của các máy đa lõi, bạn nên sử dụng đa xử lý. Tuy nhiên, phân luồng vẫn là một mô hình thích hợp nếu bạn muốn chạy đồng thời nhiều tác vụ liên kết I / O.


3

Cho phép phân luồng trong Python, vấn đề duy nhất là GIL sẽ đảm bảo rằng chỉ một luồng được thực thi tại một thời điểm (không có song song).

Vì vậy, về cơ bản nếu bạn muốn đa luồng mã để tăng tốc độ tính toán, nó sẽ không tăng tốc độ vì chỉ một luồng được thực thi tại một thời điểm, nhưng nếu bạn sử dụng nó để tương tác với cơ sở dữ liệu chẳng hạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.