Chức năng chính xác của Khóa thông dịch viên toàn cầu của Python là gì? Các ngôn ngữ khác được biên dịch sang bytecode có sử dụng cơ chế tương tự không?
Chức năng chính xác của Khóa thông dịch viên toàn cầu của Python là gì? Các ngôn ngữ khác được biên dịch sang bytecode có sử dụng cơ chế tương tự không?
Câu trả lời:
Nói chung, đối với bất kỳ vấn đề an toàn luồng nào, bạn sẽ cần phải bảo vệ cấu trúc dữ liệu bên trong của mình bằng khóa. Điều này có thể được thực hiện với nhiều mức độ chi tiết khác nhau.
Bạn có thể sử dụng khóa hạt mịn, trong đó mọi cấu trúc riêng biệt đều có khóa riêng.
Bạn có thể sử dụng khóa chi tiết thô trong đó một khóa bảo vệ mọi thứ (phương pháp GIL).
Có những ưu và nhược điểm khác nhau của mỗi phương pháp. Khóa chi tiết cho phép khả năng song song lớn hơn - hai luồng có thể thực thi song song khi chúng không chia sẻ bất kỳ tài nguyên nào. Tuy nhiên, có một chi phí quản lý lớn hơn nhiều. Đối với mỗi dòng mã, bạn có thể cần lấy và phát hành một số khóa.
Cách tiếp cận hạt thô thì ngược lại. Hai luồng không thể chạy cùng một lúc, nhưng một luồng riêng lẻ sẽ chạy nhanh hơn vì nó không thực hiện quá nhiều việc ghi sổ. Cuối cùng, nó đi đến sự cân bằng giữa tốc độ đơn luồng và song song.
Đã có một vài nỗ lực để loại bỏ GIL trong python, nhưng chi phí bổ sung cho các máy luồng đơn thường quá lớn. Một số trường hợp thực sự có thể chậm hơn ngay cả trên các máy nhiều bộ xử lý do tranh chấp khóa.
Các ngôn ngữ khác được biên dịch sang bytecode có sử dụng cơ chế tương tự không?
Nó khác nhau và có lẽ nó không nên được coi là một thuộc tính ngôn ngữ quá nhiều như một thuộc tính triển khai. Ví dụ: có các triển khai Python như Jython và IronPython sử dụng phương pháp phân luồng của máy ảo cơ bản của chúng, thay vì phương pháp GIL. Ngoài ra, phiên bản tiếp theo của Ruby có vẻ sẽ hướng tới việc giới thiệu GIL.
Sau đây là từ Hướng dẫn tham khảo API Python / C chính thức :
Trình thông dịch Python không hoàn toàn an toàn cho chuỗi. Để hỗ trợ các chương trình Python đa luồng, có một khóa toàn cầu phải được giữ bởi luồng hiện tại trước khi nó có thể truy cập các đối tượng Python một cách an toàn. Nếu không có khóa, ngay cả những thao tác đơn giản nhất cũng có thể gây ra sự cố trong một chương trình đa luồng: ví dụ: khi hai luồng đồng thời tăng số lượng tham chiếu của cùng một đối tượng, số lượng tham chiếu có thể chỉ được tăng một lần thay vì hai lần.
Do đó, quy tắc tồn tại rằng chỉ luồng đã có được khóa thông dịch toàn cục mới có thể hoạt động trên các đối tượng Python hoặc gọi các hàm API Python / C. Để hỗ trợ các chương trình Python đa luồng, trình thông dịch thường xuyên phát hành và yêu cầu lại khóa - theo mặc định, cứ mỗi 100 lệnh bytecode (điều này có thể được thay đổi bằng sys.setcheckinterval ()). Khóa cũng được phát hành và được yêu cầu lại xung quanh khả năng chặn các hoạt động I / O như đọc hoặc ghi tệp, để các luồng khác có thể chạy trong khi luồng yêu cầu I / O đang đợi hoạt động I / O hoàn tất.
Tôi nghĩ nó tóm tắt vấn đề khá tốt.
Khóa thông dịch viên toàn cục là một khóa kiểu mutex lớn để bảo vệ các bộ đếm tham chiếu không bị hosed. Nếu bạn đang viết mã python thuần túy, tất cả điều này xảy ra đằng sau hậu trường, nhưng nếu bạn nhúng Python vào C, thì bạn có thể phải lấy / giải phóng khóa một cách rõ ràng.
Cơ chế này không liên quan đến việc Python được biên dịch thành bytecode. Nó không cần thiết cho Java. Trên thực tế, nó thậm chí không cần thiết cho Jython (python được biên dịch sang jvm).
xem thêm câu hỏi này
Python, giống như perl 5, không được thiết kế từ đầu để an toàn cho luồng. Các luồng được ghép vào sau thực tế, do đó, khóa thông dịch chung được sử dụng để duy trì loại trừ lẫn nhau ở nơi chỉ có một luồng đang thực thi mã tại một thời điểm nhất định trong ruột của trình thông dịch.
Các luồng Python riêng lẻ được chính trình thông dịch xử lý đa nhiệm một cách hợp tác bằng cách lặp lại khóa thường xuyên.
Tự nắm lấy khóa là cần thiết khi bạn đang nói chuyện với Python từ C khi các luồng Python khác đang hoạt động để 'chọn tham gia' vào giao thức này và đảm bảo rằng không có gì không an toàn xảy ra sau lưng bạn.
Các hệ thống khác có di sản đơn luồng sau này phát triển thành hệ thống phân luồng thường có một số cơ chế thuộc loại này. Ví dụ, hạt nhân Linux có "Big Kernel Lock" từ những ngày đầu SMP. Dần dần theo thời gian khi hiệu suất đa luồng trở thành một vấn đề, có xu hướng cố gắng phá vỡ các loại khóa này thành các phần nhỏ hơn hoặc thay thế chúng bằng các thuật toán không khóa và cấu trúc dữ liệu nếu có thể để tối đa hóa thông lượng.
reiserfs
- lý do thực sự duy nhất tôi biết về nó).
Đối với câu hỏi thứ hai của bạn, không phải tất cả các ngôn ngữ kịch bản đều sử dụng điều này, nhưng nó chỉ làm cho chúng kém mạnh mẽ hơn. Ví dụ, các chuỗi trong Ruby có màu xanh lá cây và không phải là bản địa.
Trong Python, các luồng là bản địa và GIL chỉ ngăn chúng chạy trên các lõi khác nhau.
Trong Perl, các chủ đề thậm chí còn tồi tệ hơn. Họ chỉ sao chép toàn bộ trình thông dịch và không thể sử dụng được như trong Python.
Có lẽ đây bài báo của tờ BDFL sẽ giúp.