Tại sao Python được viết bằng GIL?


112

Khóa trình thông dịch toàn cầu (GIL) dường như thường được trích dẫn là lý do chính tại sao xâu chuỗi và tương tự là một mẹo khó hiểu trong Python - điều này đặt ra câu hỏi "Tại sao điều đó được thực hiện ngay từ đầu?"

Không phải là lập trình viên, tôi không biết tại sao điều đó có thể xảy ra - logic đằng sau việc đưa vào GIL là gì?


10
Các bài viết trên Wikipedia nói rằng "các GIL có thể là một rào cản đáng kể để xử lý song song-một giá phải trả cho việc có tính năng động của ngôn ngữ" , và tiếp tục nói rằng "Lý do sử dụng một khóa này bao gồm: tăng tốc độ của các chương trình đơn luồng (không cần thiết phải thu thập hoặc giải phóng các khóa trên tất cả các cấu trúc dữ liệu một cách riêng biệt) và tích hợp dễ dàng các thư viện C thường không an toàn cho chuỗi. "
Robert Harvey

3
@RobertHarvey, Năng động không có gì để làm với nó. Vấn đề là đột biến.
dan_waterworth


1
Không thể giúp cảm thấy giống như việc thiếu số không dấu của Java, nó nhằm ngăn chặn những người không biết họ đang tự bắn vào chân mình. Thật không may, bất cứ ai không biết những gì họ đang làm được một ngôn ngữ thiếu, đó là một sự xấu hổ thực vì đá Python trong rất nhiều cách khác
Basic

1
@Basic phải có một số cách tiêu chuẩn để xử lý các mảng byte trong Java (tôi đã không sử dụng nó trong một thời gian dài) để làm toán điện tử. Python (ví dụ) không có số đã ký, nhưng tôi thậm chí sẽ không thử thực hiện thao tác bitwise với nó vì có nhiều cách tốt hơn.
Nick T

Câu trả lời:


105

Có một số triển khai Python, ví dụ, CPython, IronPython, RPython, v.v.

Một số người trong số họ có GIL, một số thì không. Ví dụ: CPython có GIL:

Từ http://en.wikipedia.org/wiki/Global_Interpreter_Lock

Các ứng dụng được viết bằng ngôn ngữ lập trình với GIL có thể được thiết kế để sử dụng các quy trình riêng biệt để đạt được sự song song hoàn toàn, vì mỗi quy trình có trình thông dịch riêng và đến lượt nó có GIL riêng.

Lợi ích của GIL

  • Tăng tốc độ của các chương trình đơn luồng.
  • Dễ dàng tích hợp các thư viện C thường không an toàn cho chuỗi.

Tại sao Python (CPython và những người khác) sử dụng GIL

Trong CPython, khóa trình thông dịch toàn cầu, hoặc GIL, là một mutex ngăn chặn nhiều luồng gốc thực thi mã byte Python cùng một lúc. Khóa này là cần thiết chủ yếu vì quản lý bộ nhớ của CPython không an toàn cho chuỗi.

GIL gây tranh cãi vì nó ngăn các chương trình CPython đa luồng tận dụng tối đa các hệ thống đa bộ xử lý trong các tình huống nhất định. Lưu ý rằng các hoạt động có khả năng chặn hoặc hoạt động lâu dài, chẳng hạn như I / O, xử lý hình ảnh và xử lý số NumPy, xảy ra bên ngoài GIL. Do đó, chỉ trong các chương trình đa luồng dành nhiều thời gian bên trong GIL, diễn giải mã byte CPython, GIL mới trở thành nút cổ chai.

Python có GIL trái ngược với khóa hạt mịn vì nhiều lý do:

  • Nó là nhanh hơn trong trường hợp đơn luồng.

  • Nó nhanh hơn trong trường hợp đa luồng cho các chương trình ràng buộc i / o.

  • Nó nhanh hơn trong trường hợp đa luồng cho các chương trình giới hạn cpu thực hiện công việc chuyên sâu tính toán của chúng trong các thư viện C.

  • Nó làm cho các phần mở rộng C dễ viết hơn: sẽ không có chuyển đổi các luồng Python trừ khi bạn cho phép nó xảy ra (tức là giữa các macro Py_BEGIN_ALLOW_THREADS và Py_END_ALLOW_THREADS).

  • Nó làm cho thư viện C dễ dàng hơn. Bạn không phải lo lắng về an toàn luồng. Nếu thư viện không an toàn cho luồng, bạn chỉ cần giữ GIL bị khóa trong khi bạn gọi nó.

GIL có thể được phát hành bởi các phần mở rộng C. Thư viện chuẩn của Python phát hành GIL xung quanh mỗi cuộc gọi i / o chặn. Do đó, GIL không có hậu quả đối với hiệu suất của các máy chủ bị ràng buộc i / o. Do đó, bạn có thể tạo các máy chủ mạng trong Python bằng cách sử dụng các quy trình (ngã ba), luồng hoặc i / o không đồng bộ và GIL sẽ không cản trở bạn.

Các thư viện số trong C hoặc Fortran có thể được gọi tương tự với GIL được phát hành. Trong khi tiện ích mở rộng C của bạn đang chờ FFT hoàn thành, trình thông dịch sẽ thực thi các luồng Python khác. Do đó, GIL cũng dễ dàng và nhanh hơn khóa hạt mịn trong trường hợp này. Điều này tạo thành phần lớn của công việc số. Tiện ích mở rộng NumPy phát hành GIL bất cứ khi nào có thể.

Chủ đề thường là một cách xấu để viết hầu hết các chương trình máy chủ. Nếu tải thấp, việc rèn sẽ dễ dàng hơn. Nếu tải cao, i / o không đồng bộ và lập trình hướng sự kiện (ví dụ: sử dụng khung Twisted của Python) sẽ tốt hơn. Lý do duy nhất để sử dụng các luồng là thiếu os.fork trên Windows.

GIL là một vấn đề nếu và chỉ khi bạn đang thực hiện công việc đòi hỏi nhiều CPU trong Python thuần túy. Tại đây bạn có thể có được thiết kế sạch hơn bằng cách sử dụng các quy trình và truyền tin nhắn (ví dụ: mpi4py). Ngoài ra còn có một mô-đun 'xử lý' trong cửa hàng phô mai Python, cung cấp cho các quy trình giao diện giống như các luồng (tức là thay thế luồng. Đọc bằng xử lý.Process).

Các luồng có thể được sử dụng để duy trì khả năng phản hồi của GUI bất kể GIL. Nếu GIL làm suy yếu hiệu suất của bạn (xem phần thảo luận ở trên), bạn có thể để chủ đề của mình sinh ra một quy trình và chờ đợi nó kết thúc.


52
Âm thanh như nho chua với tôi. Python không thể thực hiện đúng các chủ đề, vì vậy bạn đưa ra lý do tại sao các chủ đề không cần thiết hoặc thậm chí xấu. "Nếu tải thấp, việc rèn dễ dàng hơn", nghiêm túc chứ? Và GIL "nhanh hơn" cho tất cả các trường hợp đó chỉ khi bạn khăng khăng sử dụng GC đếm giới thiệu.
Michael Borgwardt

9
s/RPython/PyPy/g. @MichaelBorgwardt Đưa ra lý do pro GIL là loại câu hỏi, phải không? Mặc dù tôi đồng ý rằng một số nội dung của câu trả lời này (cụ thể là thảo luận về các lựa chọn thay thế) nằm bên cạnh vấn đề. Và tốt hơn hay tồi tệ hơn, việc đếm tiền giờ đây gần như không thể thoát khỏi - nó đã ăn sâu vào toàn bộ cơ sở API và mã; hầu như không thể thoát khỏi nó mà không viết lại một nửa mã và phá vỡ tất cả các mã bên ngoài.

10
Đừng quên multiprocessingthư viện - tiêu chuẩn kể từ 2.6. Nhóm công nhân của nó là một sự trừu tượng siêu trơn đối với một số loại song song đơn giản.
Sean McSomething

8
@alcalde Chỉ khi bạn không biết những gì bạn đang làm và / hoặc bạn không muốn chủ đề của mình có thể hoạt động hợp tác / giao tiếp. Mặt khác, đó là một nỗi đau của hoàng gia ở phía sau, đặc biệt là xem xét chi phí khởi động một quy trình mới trên một số HĐH. Chúng tôi có máy chủ với 32 lõi, vì vậy để sử dụng chúng đầy đủ trong CPython tôi cần 32 quy trình. Đó không phải là một "giải pháp tốt", đó là một bản hack để khắc phục những bất cập của CPython.
Cơ bản

8
Thực tế là các luồng tồn tại trên các nền tảng khác ngoài Windows phải là bằng chứng đủ cho thấy việc chuyển đổi không đủ trong mọi tình huống.
zneak

42

Trước hết: Python không có GIL. Python là ngôn ngữ lập trình. Một ngôn ngữ lập trình là một tập hợp các quy tắc và hạn chế toán học trừu tượng. Không có gì trong Đặc tả ngôn ngữ Python nói rằng phải có GIL.

Có nhiều cách triển khai Python khác nhau. Một số có GIL, một số thì không.

Một lời giải thích đơn giản để có GIL là viết mã đồng thời là khó. Bằng cách đặt một khóa khổng lồ xung quanh mã của bạn, bạn buộc nó phải luôn chạy ổn định. Vấn đề được giải quyết!

Cụ thể, trong CPython, một mục tiêu quan trọng là giúp dễ dàng mở rộng trình thông dịch với các plugin được viết bằng C. Một lần nữa, viết mã đồng thời là khó, vì vậy bằng cách đảm bảo rằng sẽ không có đồng thời, việc viết phần mở rộng dễ dàng hơn thông dịch viên. Thêm vào đó, nhiều trong số các tiện ích mở rộng đó chỉ là các trình bao bọc mỏng xung quanh các thư viện hiện có có thể không được viết với sự tương tranh.


6
Đó là lập luận tương tự như việc Java thiếu các kiểu số không dấu - các nhà phát triển nghĩ rằng mọi người khác đều ngu ngốc hơn họ ...
Cơ bản

1
@Basic - tin hay không, ngay cả khi bạn không thực sự, thực sự ngu ngốc, hóa ra việc có một ngôn ngữ đơn giản hóa các giả định có nghĩa là bạn không nghĩ về một số điều để làm cho chúng hoạt động vẫn hữu ích Điều. CPython rất tốt cho một số thứ nhất định, bao gồm các ứng dụng đa luồng đơn giản (trong đó chương trình bị ràng buộc IO, nhiều thứ, và do đó GIL không thành vấn đề), vì các quyết định thiết kế giúp GIL trở thành giải pháp tốt nhất cũng giúp lập trình các ứng dụng đó dễ dàng hơn , đặc biệt là thực tế là nó hỗ trợ các hoạt động nguyên tử trên các bộ sưu tập .
Jules

@Jules Vâng, nó rất tiện dụng cho đến khi bạn cần những khả năng đó. Giải pháp "ưa thích" của cpython là "chỉ cần viết nó bằng ngôn ngữ khác như c ++" sau đó có nghĩa là bạn mất mọi lợi ích của con trăn đơn lẻ. Nếu bạn đang viết một nửa mã của mình bằng c ++, thì tại sao lại bắt đầu từ Python? Chắc chắn, đối với các dự án API / keo nhỏ, nó nhanh chóng và dễ dàng, và đối với ETL, nó không phải là thứ hai, nhưng nó không phù hợp với bất cứ điều gì đòi hỏi phải nâng vật nặng. Tương tự như việc sử dụng Java để nói chuyện với phần cứng ... Nó gần như là những trò đùa mà bạn phải vượt qua.
Cơ bản

16

Mục đích của GIL là gì?

Tài liệu của CAPI có điều này để nói về chủ đề này:

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, được gọi là khóa trình thông dịch toàn cầu hoặc GIL, 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ả các thao tác đơn giản nhất cũng có thể gây ra sự cố trong chương trình đa luồng: ví dụ: khi hai luồng đồng thời tăng số tham chiếu của cùng một đối tượng, thì số tham chiếu có thể chỉ tăng lên một lần thay vì hai lần.

Nói cách khác, GIL đang ngăn chặn tham nhũng của nhà nước. Các chương trình Python không bao giờ tạo ra lỗi phân đoạn, vì chỉ cho phép các hoạt động an toàn bộ nhớ. GIL mở rộng sự đảm bảo này cho các chương trình đa luồng.

Các lựa chọn thay thế là gì?

Nếu mục đích của GIL là bảo vệ nhà nước khỏi tham nhũng, thì một sự thay thế rõ ràng là khóa ở một hạt nhỏ hơn nhiều; có lẽ ở cấp độ đối tượng. Vấn đề với điều này là mặc dù nó đã được chứng minh là làm tăng hiệu suất của các chương trình đa luồng, nhưng nó có nhiều chi phí hơn và các chương trình đơn luồng phải chịu hậu quả.


2
Sẽ thật tuyệt khi cho phép người dùng chạy một chương trình với tùy chọn trình thông dịch thay thế gil cho khóa hạt mịn và bằng cách nào đó biết - theo cách chỉ đọc - cho dù quy trình hiện tại được đưa ra có hoặc không có gil.
Luis Masuelli

Mặc dù GIL tôi đã cố gắng tạo ra lỗi phân đoạn trong một chương trình đa luồng do sử dụng mô-đun pyodbc bất cẩn. Do đó "không bao giờ nên tạo ra lỗi phân khúc" là sai lầm.
Muposat
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.