Gửi tín hiệu tiêu diệt đến chuỗi python
def do_kill_pydev_thread(self): PyDBDaemonThread.do_kill_pydev_thread(self) # Note that we no longer shutdown the reader, just the writer. The idea is that we shutdown # the writer to send that the communication has finished, then, the client will shutdown its # own writer when it receives an empty read, at which point this reader will also shutdown. # That way, we can *almost* guarantee that all messages have been properly sent -- it's not # completely guaranteed because it's possible that the process exits before the whole # message was sent as having this thread alive won't stop the process from exiting -- we # have a timeout when exiting the process waiting for this thread to finish -- see: # PyDB.dispose_and_kill_all_pydevd_threads()). # try: # self.sock.shutdown(SHUT_RD) # except: # pass # try: # self.sock.close() # except: # pass Bài đăng này là để đề xuất một tính năng hiếm khi có trong các ngôn ngữ khác, mà tôi cho rằng có thể bị nhiều người coi là “lập trình tồi”. Tôi biết rằng chủ đề cuối cùng có thể được coi là đi ngược lại kiến thức chung về các thông lệ tốt liên quan đến phân luồng. Tôi viết điều này để giải thích lý do tại sao tôi coi tính năng được mô tả là lập trình tốt và thậm chí là một tính năng rất thú vị mà trăn có thể có Show Đây là tất cả về tương tác luồng điều khiển giữa các luồng và theo cách an toàn Cách lý tưởng & trực quan so với thực tế tàn khốcHãy bắt đầu với một ví dụ. Giả sử tôi có hai luồng và tôi muốn báo hiệu hoặc dừng luồng thứ hai
Chà, điều này là không thể trong python khi sử dụng API luồng được đề xuất. Điều này có thể thực hiện được ở các ngôn ngữ khác bằng cách giết luồng theo cách chúng ta giết một tiến trình. Nhưng đây không phải là một ý tưởng tuyệt vời để tiếp cận giải pháp này cho các hoạt động bình thường với các quy trình và đây là một ý tưởng thậm chí còn tồi tệ hơn khi thay vì hủy các quy trình có tài nguyên bộ nhớ riêng biệt, bạn hủy một luồng chia sẻ bộ nhớ và tài nguyên với các luồng khác của nó. Thông thường, đưa ra API luồng hiện tại, người ta khuyên bạn nên tạo một cờ và thay vào đó để luồng tự thoát
Bạn có thể nhận thấy chúng tôi phải thay đổi 2 điều quan trọng từ mã trước đó
Vì vậy, để kết luận, thủ thuật gắn cờ bắt buộc bạn phải viết lại chủ đề của mình dưới dạng một vòng lặp và đây là tất cả nội dung của bài đăng này. bởi vì nhiều nhiệm vụ không thể được viết lại một cách có ý nghĩa như một vòng lặp. Có những tác vụ không có gì lặp lại, nhưng các thao tác chỉ tốn thời gian và chúng tôi muốn dừng luồng vì nhiều lý do (tiết kiệm tài nguyên tính toán/vật lý, tránh luồng làm điều gì đó không muốn nữa nếu luồng đang quản lý một số Người ta có thể lập luận rằng với bất kỳ tác vụ không lặp nào, bạn có thể viết lại nó dưới dạng một vòng lặp bằng cách sử dụng mẫu của máy trạng thái (như ví dụ này hoặc trường hợp chuyển đổi trần), hoặc điều gì đó tương tự với hệ thống gọi lại sự kiện hoặc một tập hợp . Chà, tất cả điều này rất xâm phạm đến cấu trúc mã của tác vụ mong muốn
Nhìn vào cách giải quyết vấn đề này bằng các ngôn ngữ và thư viện khác nhau, chúng ta có thể thấy rằng. Người dùng C sẽ chọn máy trạng thái trường hợp chuyển đổi hoặc vòng lặp có điều kiện, người dùng C# sẽ chọn gọi lại sự kiện, Qt thực thi gọi lại sự kiện (ngay cả khi chỉ có 1 luồng), GTK cũng vậy, nodejs cũng vậy, người dùng python sẽ chọn giữa các . Mỗi lần, việc lắp một trong 3 thiết kế này sẽ đòi hỏi rất nhiều công sức từ người dùng chưa quen với nó hoặc những người muốn lập trình một tác vụ không phù hợp với các thiết kế chia mã này (như trường hợp dài đã đề cập ở trên). Không phải nói rằng các vòng lặp có điều kiện, máy trạng thái và hệ thống gọi lại sự kiện là tệ (tôi là một fan hâm mộ tuyệt vời của chúng trong những trường hợp khác), chỉ là chúng không liên quan đến các tác vụ không thể phân chia cụ thể vì nhiều lý do Đây là những chủ đề có thể bị gián đoạn một tia hy vọng với sự gián đoạnÝ tưởngÝ tưởng xuất phát từ thế giới vi mô nơi không có luồng, nhưng vấn đề về các trường hợp bất ngờ xảy ra bất kỳ lúc nào trong mã phải được giải quyết (các trường hợp do chương trình hoặc nhiều khả năng là do thế giới thực). Trên mọi microship trên thế giới, chúng ta có thể làm như sau. (lấy API arduino để ẩn việc triển khai bằng các thanh ghi)
Tất cả điều này đang hoạt động trên một luồng duy nhất (luồng duy nhất của microship) và điều này cũng có thể thực hiện được trên mọi CPU máy tính khi viết mã ở cấp hạt nhân Làm thế nào để giúp đỡ trong trường hợp của chúng tôi? . Tôi đề xuất rằng chúng tôi triển khai loại chức năng ngắt này trong chuỗi python. Điều này có thể giống như sau
Vì vậy, chúng tôi có thể lập trình này
Thực hiệnNgười ta có thể tự hỏi sau tất cả những cân nhắc bí truyền này, liệu điều này có thể thực hiện được không. câu trả lời là có. Cả cho python và cho các ngôn ngữ được biên dịch đã cung cấp ngoại lệ Đã có một cách để triển khai nó mà không cần viết một dòng mã C nào, sử dụng từ API CPython. Hạn chế duy nhất là nó chỉ có thể gửi các loại ngoại lệ đến luồng để ngắt và do đó không thể có thêm dữ liệu trong ngoại lệ, nhưng chúng ta có thể giải quyết vấn đề đó Cách ngắt đầu tiên này hiện được gắn với triển khai CPython hiện tại bằng GIL. Vì vậy, chúng tôi có thể sợ rằng các tính năng của anh ấy không thể được chuyển sang các triển khai python khác ngay bây giờ hoặc trong tương lai. May mắn thay, đây không phải là vấn đề, vì các luồng posix được thiết kế để được thông báo bằng cách sử dụng các tín hiệu cấp hệ điều hành (số nguyên đơn giản) thực sự hoạt động trong các luồng theo cách giống như các ngắt trên vi mạch. Ví dụ tốt nhất là tín hiệu 9 được gửi đến luồng chính của chương trình khi chúng tôi nhập 0 trong trình bao. Ngoài 9, luồng posix cho phép gửi rất nhiều loại tín hiệu (bao gồm một số tín hiệu do người dùng xác định) từ quy trình này sang quy trình khác và từ luồng này sang luồng khácTrên thực tế, các luồng posix cho phép có một nửa những gì tôi đề xuất cho các luồng có thể bị gián đoạn trong bất kỳ chương trình C nào. Các tín hiệu luồng chỉ tạm dừng và tiếp tục các hoạt động của luồng, ngược lại với các ngoại lệ dừng dần dần mọi thứ cho đến khi tìm thấy khung ngăn xếp nơi xử lý ngoại lệ. Đối với những gì liên quan đến C, hoạt động dừng khi nhận tín hiệu có thể được thực hiện kết hợp giữa tín hiệu và tiêu chuẩn 2. Đây là một ví dụ (mặc dù rất không an toàn, vì mọi thứ liên quan đến luồng trong C)Hầu hết các hệ điều hành trên thế giới đều triển khai các luồng posix, Windows với cổng này cũng vậy. Và đối với các luồng riêng của Windows không hỗ trợ tín hiệu posix, vẫn có cách để nhận các gián đoạn thông qua các ngoại lệ có cấu trúc C/C++ của chúng Có thể bạn quan tâmĐiều này có nghĩa là ngay cả trong một tương lai tuyệt vời khi GIL đã bị xóa, các luồng có thể bị gián đoạn vẫn có thể hoạt động và ổn. Nó chỉ đơn giản là yêu cầu làm lại một chút cách xử lý của 3 của trăn để cho phép các 4 nhận và gửi tín hiệu đã chọn giữa chúngtriển khai python của các luồng có thể bị gián đoạn bằng cách sử dụng luồng posix có thể hoạt động theo cách đó
Bất ngờ phải được xử lý thông qua Ngoại lệluôn có ngoại lệBây giờ bạn có thể chỉ ra rằng tất cả những điều này là không cần thiết nếu điều kiện 8 không bao giờ xảy ra bởi vì lập trình viên đã viết một số mã tốt đến mức không có gì bất ngờ có thể xảy ra. Chà, tôi sẽ trả lời rằng không có gì trong thế giới thực mà không có những sự kiện bất ngờ. Tất nhiên, thế giới thực thì không, vì vậy việc viết một chương trình tương tác với những thứ vật lý (bất kỳ thiết bị phần cứng nào, kết nối mạng có thể bị ngắt kết nối, v.v., máy có nhiều hiệu ứng, rô bốt và ô tô tự lái) thực sự cần một hệ thống xử lý ngoại lệ tốt. Ngay cả thế giới bên trong được kiểm soát và dự đoán được của máy tính cũng có thể xảy ra sự kiện bất ngờ. bạn có phiền không nếu hệ điều hành máy tính của bạn gặp sự cố khi một chương trình gặp lỗi phân tách? . May mắn thay, trong thế giới bên trong của máy tính, các sự kiện bất ngờ rất hiếm khi xảy ra, cho phép tất cả HĐH được viết bằng ngôn ngữ mà không cần xử lý ngoại lệ chuyên dụng (như C). Ngay cả với một hệ thống rất thông minh (như con người), thực hiện một nhiệm vụ mà nó đã quen (như di chuyển trên đường phố) trong một môi trường tương đối bình thường; . Vì vậy, trong kết luận, chỉ có Chúa sẽ không có ngoại lệlập trình async không giúp được gì nhiềuBạn cũng có thể thắc mắc tại sao điều này phải trải qua các ngoại lệ/ngắt chứ không phải là một hệ thống giống như không đồng bộ nơi cú pháp ngôn ngữ tự động phân tách mã thành một máy trạng thái và các sự kiện của nó được xử lý bởi trình quản lý sự kiện. Tôi không nghĩ điều đó phù hợp vì chủ yếu là 3 lý do
tất cả như ngoại lệÝ kiến của tôi là hệ thống ngoại lệ python không chỉ là một thủ thuật gỡ lỗi (nhưng rất thuận tiện để gỡ lỗi. ), nhưng là một hệ thống rất tốt để quản lý trường hợp không mong muốn, xảy ra do sự không chắc chắn trong thiết kế chương trình (như lỗi) mà còn do sự không chắc chắn trong ngữ cảnh thực thi chương trình, đặc biệt là thế giới thực. Các ngoại lệ của Python giống như một 1 hoàn toàn an toàn di chuyển ngăn xếp cuộc gọi cho đến khi tìm thấy một phạm vi có thể xử lý tình huống. Điều này rất mạnh mẽThử nghiệm chủ đề gián đoạnHiện tại, tôi đã triển khai các chủ đề như vậy trên đầu trang của 4 bằng cách sử dụng 8 đã nói ở trên vì điều đó thật dễ dàng. Tôi có thể chia sẻ mã này nếu bạn muốnTrong trường hợp của riêng tôi, tôi đang làm việc trong một dự án người máy tiên tiến, liên quan đến nhiều cảm biến, hiệu ứng và tất nhiên là rất nhiều tính toán liên quan đến học sâu, lập kế hoạch đường đi, v.v. Vì vậy, nó đang trộn nhiều thư viện lại với nhau và chạy rất nhiều tài nguyên, Đó là lý do tại sao tôi bắt đầu nghĩ đến các luồng có thể bị gián đoạn. Tôi cá rằng hầu hết các dự án trong lĩnh vực này hoặc trong lĩnh vực xe tự lái có thể có cùng nhu cầu. Cũng như các phần mềm máy chủ phải xử lý các sự cố kết nối, với các yêu cầu máy khách không hợp lệ, v.v. Tôi đã sử dụng python trong nhiều năm nhưng chỉ một tháng trước, tôi nhận ra rằng mình có thể sử dụng các chuỗi gián đoạn (vì nhu cầu của tôi ngày càng cao hơn trong năm qua). Tôi phải nói rằng nó hoạt động khá tốt và đơn giản hóa rất nhiều luồng điều khiển trong các chương trình của tôi. Tôi nghĩ rằng điều này có thể giúp ích rất nhiều cho nhiều ứng dụng phân luồng bằng Python Bạn nghĩ gì về nó ?Tôi hy vọng tôi đã mô tả rõ ràng ý tưởng và mối quan tâm đằng sau các chủ đề bị gián đoạn. Tôi nghĩ rằng tôi đã không quên những cách tốt hơn để báo hiệu/dừng luồng (có sẵn trong Python hoặc không), nếu vậy hãy cho tôi biết. Nếu bạn có quan sát, phê bình hoặc thậm chí lời khuyên, bạn đều được chào đón @python-developpers . nếu bạn đồng ý về sự quan tâm của tính năng đó, bạn có nghĩ rằng chúng tôi có thể triển khai nó trong phiên bản tiếp theo của CPython không? Tôi không phải là chuyên gia về chủ đề, nhưng tôi muốn nhận xét về một vài điều
Nếu bạn muốn đi ngược lại quan điểm phổ biến, chỉ nói rằng các chuỗi có thể ngắt là tốt là chưa đủ, bạn cần giải thích lý do tại sao quan điểm phổ biến rằng các chuỗi có thể ngắt là xấu lại thực sự không chính xác Nói cách khác, bạn cần bác bỏ các lập luận chống lại các chủ đề có thể bị gián đoạn, chứ không chỉ phản đối chúng. Tôi sẽ thừa nhận, tôi chưa bao giờ hiểu tại sao các luồng không thể bị gián đoạn, nhưng bất kể lý do là gì, bạn phải bác bỏ những lý do đó và chỉ ra lý do tại sao chúng sai, không chỉ đơn thuần tranh luận bằng cách loại suy rằng vì mã máy CPU đơn luồng có thể bị gián đoạn,
Tôi không hiểu ý của bạn khi nói "con trỏ/tham chiếu" và "biến tự do" ở đây Tất cả những lời chỉ trích của bạn về luồng có vẻ hợp lệ. Đó là một lập luận chống lại luồng, nó không bác bỏ quan điểm phổ biến rằng các luồng có thể bị gián đoạn là nguy hiểm hoặc có hại
Làm thế nào để một người gọi 5 từ API C mà không cần viết mã C?Điều gì xảy ra nếu hai hoặc nhiều chủ đề tự đăng ký để nhận SIGEXC?
Tất nhiên, có thể sơ đồ lập luận của tôi kém, nhưng trong phần còn lại của bài đăng, tôi muốn chứng minh rằng nó tốt vì mọi thứ tôi có thể thực hiện một cách an toàn cho việc quản lý tài nguyên và bộ nhớ
Vâng, đây không phải là chính xác quan điểm của tôi. Tôi lấy cảm hứng từ máy CPU đơn luồng, nhưng đó chỉ là cảm hứng. Nhưng trong phần xem xét sau đây, tôi dự định sẽ giải thích lý do tại sao tôi nghĩ đây có thể là một ý kiến hay khi chuyển nó sang python…
đơn giản thế này
Điều này có thể thực hiện được khi sử dụng ctypes, vì ctypes đã bao bọc tất cả các tiêu đề CPython C
Không có gì sai nên xảy ra. mỗi luồng tự đăng ký để nhận SIGEX chỉ gửi cho chính nó một cách rõ ràng. Vì vậy, nếu bạn gửi SIGEXC tới một luồng, thì không có luồng nào khác sẽ nhận được nó (theo thông số kỹ thuật của luồng posix)
Tôi sợ rằng sự gián đoạn của chương trình Python là không an toàn và trình xử lý tín hiệu phải cực kỳ hạn chế về những gì nó có thể làm. Nếu tôi hiểu chính xác, trình xử lý tín hiệu sẽ không mong đợi các cấu trúc dữ liệu Python ở trạng thái nhất quán. …vì vậy nó không nên thay đổi dòng mã Python
@matmel, tôi sẽ xem cái này sau. Trong thời gian chờ đợi, sau đây là cách tôi nghĩ rằng việc xử lý tín hiệu có thể được thực hiện xung quanh @vbrozik tôi đồng ý. một tín hiệu nhận được ở cấp độ C có thể xảy ra bất cứ lúc nào, chẳng hạn như trong quá trình phân bổ đối tượng hoặc hoạt động đếm tham chiếu, v.v. Tôi đề nghị chia xử lý tín hiệu thành 2 phần. gọi lại cấp độ C và gọi lại cấp độ python
tuy nhiên đây là một vấn đề khác tôi nghĩ. vấn đề không phải là làm hỏng trình thông dịch python, mà là cắt luồng python tại thời điểm chúng tôi không muốn. Tôi nghĩ rằng giải pháp cho vấn đề này là tạm dừng gián đoạn (tất cả các tín hiệu, bao gồm cả SIGINT) đối với chuỗi nhạy cảm như vậy 0 đều được xử lý sau đó
Trên thực tế, ý tưởng về hệ thống ngắt này có được tán thành hay không. Không phải câu lệnh 1 luôn khóa ngắt bàn phím, vì vậy nó không bao giờ có thể xảy ra trong một trong số 2, 0, 4 (hoặc chỉ khi python bị chặn ở một trong số đó)?Vâng, đây là một trong những cái bẫy cổ điển. nó gần như hữu ích, nó có vẻ hoạt động tốt trong nhiều trường hợp, nhưng nếu bạn muốn phần mềm của mình thực sự đáng tin cậy thì loại hủy chuỗi này chỉ… không thể thực hiện được. Và đôi khi bạn không phát hiện ra điều đó cho đến khi bạn đã vận chuyển nó. (Giống như đã xảy ra và. Họ hối tiếc) Ví dụ
Và đặt một 5 bên trong khối 6 không giúp được gì, bởi vì ngắt có thể xảy ra ngay trước lệnh gọi tới 5Hoặc xem xét 0Giả sử điều này bị hủy bỏ. Không có gì. Bạn chỉ cần nhìn vào danh sách 8 để xem công việc nào đã thực hiện và công việc nào chưa. Ngoại trừ… điều đó không hiệu quả, bởi vì bạn không thể phân biệt giữa một ngắt kích hoạt trước hoặc sau 9. Và điều này áp dụng cho bất kỳ thao tác trạng thái không tầm thường nào mà chương trình của bạn có thể thực hiện. Mayyyybe nếu bạn kiểm tra mọi nơi trong chương trình của mình và tất cả các thư viện của bên thứ ba, có bất kỳ tác dụng phụ nào và cẩn thận vô hiệu hóa các ngắt, v.v. , bạn có thể làm cho một chương trình của mình hoạt động, nhưng không ai có thể sống như vậy(Hủy chuỗi thực sự hoạt động tốt trong Haskell, vì bạn có thể đảm bảo rằng chuỗi bạn đang hủy không có tác dụng phụ có thể xảy ra. Nhưng Python không phải là Haskell. -). ) Chúng tôi chủ yếu xử lý nó trong 7, bởi vì hầu hết các chương trình thoát sau 7, vì vậy mọi trạng thái bị hỏng sẽ bị xóa sạch. Và nếu một lần trong 100 lần, nó sẽ làm hỏng trạng thái chương trình của bạn và khiến chương trình gặp sự cố… chà, sự cố cũng giống như thoát, vì vậy người dùng sẽ không quá khó chịu chứ? . )IMO 5 không nên tồn tại. Không có cách nào để viết các chương trình đáng tin cậy bằng cách sử dụng nó và nó thậm chí không phải là cách thực hiện tốt việc hủy bỏ luồng, bởi vì nếu một luồng bị kẹt khi chờ một số I/O, bạn không thể ngắt nó theo cách này. Nó đã được sử dụng bởi vì nó đã được thêm vào như một loại mánh lới quảng cáo thử nghiệm vào năm 2003 và tôi nghĩ IDLE đã từng sử dụng nó? Đây đều là những điểm rất hay, bao gồm cả những gì trên blog của bạn liên quan đến Ctrl+C… 9 của Python chỉ đáng tin cậy ở mức 99%, do đó, đó là bộ ba hoặc bất kỳ thiết kế không đồng bộ nào, do đó, mọi xử lý tín hiệu được viết bằng python thuần Nếu bạn không ngại dành nhiều thời gian hơn cho tôi về chủ đề này, chúng ta có thể có nhiều lựa chọn hơn để khám phá. Nhưng tôi sợ rằng điều này sẽ phức tạp hơn để thực hiện so với những gì tôi đã đề xuất trước đây… Tôi đã định từ bỏ chủ đề này trước khó khăn, nhưng vì tôi đã đề xuất ý tưởng và tôi nghĩ rằng việc đưa python lên 110% 9 trở nên đáng tin cậy 100% thì sao? Trước hết, trường hợp này 1không thể giúp được, bất cứ điều gì chúng tôi làm, vì những gì trước khi thử không có nghĩa là một phần của nó, và vì vậy ngay cả với những sửa đổi lớn nhất trong python, bây giờ chúng tôi không thể biết liệu việc mua lại khóa có được dọn sạch hay không. Những gì tôi sẽ đề xuất không giải quyết trường hợp này Vấn đề trong việc xử lý tín hiệu, sau khi CPython đã bảo mật tất cả các chức năng cấp độ C của nó, đó là một tín hiệu có thể xảy ra trong bất kỳ lệnh mã byte nào và khi trình thông dịch đang kiểm tra giữa bất kỳ lệnh nào, nó có thể xuất hiện ở bất kỳ vị trí nào trong mã của chúng tôi. Một tùy chọn để khám phá là. chúng ta có thể thay đổi thực tế này rằng trình thông dịch đang kiểm tra giữa mọi hướng dẫn không? Giả sử chúng ta có thêm 2 opcodes
Và bằng cách sử dụng các hướng dẫn này, chúng tôi đảm bảo tại quá trình tạo mã byte mà các mã lệnh trước đó luôn bao bọc. câu lệnh 6 và gọi tới ____5_______0 và 4 trong câu lệnh 1. Và bất kỳ thao tác GC nào như các phương thức 2 cũng vô hiệu hóa các ngắt theo cách mà các mã lệnh đó thực hiệnCác ví dụ sau được sửa đổi bytecode của python 3. 8
Những ví dụ đó sẽ khắc phục sự cố của 7 trong quá trình dọn dẹp, miễn là việc dọn dẹp chỉ được thực hiện trong trình quản lý bối cảnh hoặc trong khối cuối cùng. Và sẽ có những lúc chương trình python không thể phản hồi ngay lập tức với 9, và điều đó sẽ chỉ tốt thôi. (Và điều đó không nên quá lâu vì các quy trình như vậy để phân bổ, quản lý bối cảnh, v.v. thường diễn ra nhanh chóng từ góc độ con người). Trong trường hợp hiếm hoi mà người dùng đã thực hiện một số thao tác dài đối với một trong những quy trình đó, chương trình có thể mất nhiều thời gian để phản ứng với 9. Ý kiến của tôi là nếu người dùng muốn dừng giữa chừng một quy trình quan trọng theo thiết kế, thì không có lựa chọn an toàn nào khác ngoài việc dừng toàn bộ quy trình và gửi cho họ một 8 thay vì một ____2_______9 (vì việc gián đoạn rất có khả năng làm hỏng . Một cuộc gọi lại cấp độ C vẫn có thể xuất ra dấu vết ngược của python trong trường hợp như vậy. Vì vậy, điều này có thể được triển khai trong cuộc gọi lại tín hiệu C, nếu nhận được nhiều 9 trong một thời gian ngắn hoặc điều này có thể tùy thuộc vào trình bao để có một phím tắt khác gửi 8Để linh hoạt hơn một chút trong các tuyên bố đó (trong trường hợp chúng tôi không chỉ sử dụng chúng để dọn dẹp), chúng tôi có thể thêm hai trình quản lý bối cảnh toàn cầu nối
Sau đây là một ví dụ phức tạp về vùng khởi tạo-dọn dẹp lồng nhau và vùng không dọn dẹp 6 7Tôi không biết opcodes cho câu lệnh 1 và 6 trong các phiên bản mới hơn của python là gì, nhưng nếu có opcodes cụ thể cho các khối bắt đầu và kết thúc, lệnh gọi tới 4 cũng có thể được tích hợp trong các opcodes đó, vì vậy mã byte |