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ó

Đâ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ốc

Hã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

# in a perfect world

def myfunction():
    # .. do something, very long, very complex, and very nested
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread.terminate()

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

# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True

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 đó

  • chúng tôi đã tạo một biến cờ mà chúng tôi cần chia sẻ giữa các luồng, Nó có thể được chia sẻ bằng cách sử dụng biến toàn cục (không được khuyến nghị) hoặc sử dụng con trỏ/tham chiếu hoặc sử dụng biến miễn phí vì python có tính năng đó… hoặc thậm chí sử dụng thuộc tính của đối tượng luồng nếu bạn

    Vì vậy, thay đổi này thường không phải là vấn đề lớn trong python, nhưng nó giả định rằng mọi chức năng cần dừng chuỗi có thể truy cập biến cờ này (và đây là lý do tại sao các biến toàn cục thường được sử dụng thay vì freevars)

  • chúng tôi đã thay đổi phần thân chức năng của luồng thành một vòng lặp, kiểm tra cờ ở mỗi lần lặp lại. Ngoài ra, chúng tôi có thể giữ cùng một nội dung chức năng và đặt xác minh giữa mỗi dòng mã ban đầu nhưng điều này có thể còn tệ hơn và bất tiện. (đây là một ví dụ khiến bạn phải bật khóc với ý tưởng này)

    def myfunction():
       if mythread_exit:  return
       do_some_stuff()
       if mythread_exit:  return
       yet_more_stuff()
       if mythread_exit:  return
       if some_condition():
           if mythread_exit:  return
       	do_an_action():
           if mythread_exit:  return
       and_yet()
       if mythread_exit:  return
    

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

  • Điều này sẽ luôn dẫn đến một lượng mã rất lớn nếu tác vụ phải có khả năng bị gián đoạn ở nhiều điểm (bạn sẽ chia tác vụ thành một chức năng trên mỗi dòng mã gốc và viết một trạng thái hoặc một lệnh gọi lại trên mỗi dòng mã gốc)
  • Ngoài ra, bạn có thể muốn dừng/báo hiệu luồng trong một hoạt động dài chưa được bạn thực hiện mà bởi một số thư viện khác (và bạn không thể thay đổi mã này để phù hợp với trạng thái hoặc lệnh gọi lại)

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)

void mycallback() {
    // do something, once the callback is finished, the execution of the main continues
}

main() {
    attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
    attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
    
    // do my complex, long, very nested stuff
    // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
    
    nit();  // disable interruption (interruptions signals will wait for further enable)
    // do some sensitive stuff, that must not be paused
    eit(); // reenable interruption
}

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

class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''

Vì vậy, chúng tôi có thể lập trình này

def myfunction():
    with currenthread().nointerrupts():
        # .. do critical stuff here that shall not be interrupted
    # .. do something, very long, very complex, and very nested, that can be interrupted
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread.terminate()  # alias to   mythread.interrupt(SystemExit)
    # also possible with any exception
    mythread.interrupt(MyCustomException('time to react, thread!'))  
result = mythread.wait()

Thực hiện

Ngườ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

# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
9 được gửi đến luồng chính của chương trình khi chúng tôi nhập
def myfunction():
   if mythread_exit:  return
   do_some_stuff()
   if mythread_exit:  return
   yet_more_stuff()
   if mythread_exit:  return
   if some_condition():
       if mythread_exit:  return
   	do_an_action():
       if mythread_exit:  return
   and_yet()
   if mythread_exit:  return
0 trong trình bao. Ngoài
# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
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ác

Trê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

def myfunction():
   if mythread_exit:  return
   do_some_stuff()
   if mythread_exit:  return
   yet_more_stuff()
   if mythread_exit:  return
   if some_condition():
       if mythread_exit:  return
   	do_an_action():
       if mythread_exit:  return
   and_yet()
   if mythread_exit:  return
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

Đ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

def myfunction():
   if mythread_exit:  return
   do_some_stuff()
   if mythread_exit:  return
   yet_more_stuff()
   if mythread_exit:  return
   if some_condition():
       if mythread_exit:  return
   	do_an_action():
       if mythread_exit:  return
   and_yet()
   if mythread_exit:  return
3 của trăn để cho phép các
def myfunction():
   if mythread_exit:  return
   do_some_stuff()
   if mythread_exit:  return
   yet_more_stuff()
   if mythread_exit:  return
   if some_condition():
       if mythread_exit:  return
   	do_an_action():
       if mythread_exit:  return
   and_yet()
   if mythread_exit:  return
4 nhận và gửi tín hiệu đã chọn giữa chúng

triể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 đó

  • luồng chính không còn nhận tất cả các tín hiệu hệ điều hành nữa, nhưng hãy để các luồng nhận một tín hiệu cụ thể, ví dụ như gọi nó là
    def myfunction():
       if mythread_exit:  return
       do_some_stuff()
       if mythread_exit:  return
       yet_more_stuff()
       if mythread_exit:  return
       if some_condition():
           if mythread_exit:  return
       	do_an_action():
           if mythread_exit:  return
       and_yet()
       if mythread_exit:  return
    
    5
  • khi một luồng python đang khởi tạo, nó sẽ đăng ký gọi lại tín hiệu C hoặc
    def myfunction():
       if mythread_exit:  return
       do_some_stuff()
       if mythread_exit:  return
       yet_more_stuff()
       if mythread_exit:  return
       if some_condition():
           if mythread_exit:  return
       	do_an_action():
           if mythread_exit:  return
       and_yet()
       if mythread_exit:  return
    
    5
  • khi làm gián đoạn một luồng, python đặt ngoại lệ trong danh sách chờ của luồng đích và gửi một
    def myfunction():
       if mythread_exit:  return
       do_some_stuff()
       if mythread_exit:  return
       yet_more_stuff()
       if mythread_exit:  return
       if some_condition():
           if mythread_exit:  return
       	do_an_action():
           if mythread_exit:  return
       and_yet()
       if mythread_exit:  return
    
    5 đến luồng khác
  • khi lệnh gọi lại C của luồng đích nhận được tín hiệu, quá trình triển khai luồng hệ điều hành sẽ tạm dừng quá trình thực thi luồng, bao gồm cả mã python. cuộc gọi lại kiểm tra xem luồng có bật ngắt không
    • nếu được bật. nó đặt các ngoại lệ đã nhận cho lần tiếp theo trình thông dịch luồng kiểm tra các ngoại lệ
    • nếu bị vô hiệu hóa. nó ngăn xếp ngoại lệ nhận được để kiểm tra khả năng kích hoạt lại
  • khi một luồng ngoại lệ có thể kích hoạt lại, nó sẽ kiểm tra các ngoại lệ đã nhận được xếp chồng lên nhau, nếu có, nó sẽ gọi các cuộc gọi lại python đã đăng ký trước (nếu có), sau đó đặt ngoại lệ trong quy trình hoạt động bình thường và tiếp tục quy trình

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

def myfunction():
   if mythread_exit:  return
   do_some_stuff()
   if mythread_exit:  return
   yet_more_stuff()
   if mythread_exit:  return
   if some_condition():
       if mythread_exit:  return
   	do_an_action():
       if mythread_exit:  return
   and_yet()
   if mythread_exit:  return
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ệ
Gửi tín hiệu tiêu diệt đến chuỗi python

lập trình async không giúp được gì nhiều

Bạ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

  • Cú pháp ngôn ngữ sẽ tạo ra một máy trạng thái hoặc bất kỳ sự phân chia mã nào về mặt lý thuyết có thể được coi là một máy trạng thái. Như vậy, bất kỳ sự gián đoạn nào xảy ra ở giữa một trong các khối mã trạng thái của nó sẽ đợi thủ tục trạng thái hoàn tất trước khi ngắt. với asyncio, điều này có nghĩa là việc gọi một hàm không đồng bộ sẽ làm cho chuỗi của bạn không bị gián đoạn cho đến khi hàm này quay trở lại. Tất nhiên đó là một vấn đề lớn vì hầu hết các chức năng tính toán không (và sẽ không) được triển khai dưới dạng không đồng bộ
  • Điều này sẽ yêu cầu viết hầu hết mã của bạn theo cú pháp không đồng bộ, nghĩa là bạn sẽ đặt
    def myfunction():
       if mythread_exit:  return
       do_some_stuff()
       if mythread_exit:  return
       yet_more_stuff()
       if mythread_exit:  return
       if some_condition():
           if mythread_exit:  return
       	do_an_action():
           if mythread_exit:  return
       and_yet()
       if mythread_exit:  return
    
    9 trước mỗi lệnh gọi hàm để luồng của bạn có thể bị gián đoạn ở bất kỳ dòng nào. trong khi dễ đọc hơn một chút so với
    void mycallback() {
        // do something, once the callback is finished, the execution of the main continues
    }
    
    main() {
        attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
        attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
        
        // do my complex, long, very nested stuff
        // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
        
        nit();  // disable interruption (interruptions signals will wait for further enable)
        // do some sensitive stuff, that must not be paused
        eit(); // reenable interruption
    }
    
    0 có mặt ở khắp nơi đã nói ở trên thì nó chắc chắn là rườm rà để viết
  • Python là một ngôn ngữ được giải thích, do đó các hoạt động riêng lẻ đã được phân chia ở cấp mã byte và trạng thái ngoại lệ đã được kiểm tra sau mỗi hoạt động. Vì vậy, việc nhận các ngắt dưới dạng ngoại lệ không mang lại thêm chi phí nào cho các hoạt động bình thường trái ngược với việc chờ đợi mọi thứ

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

void mycallback() {
    // do something, once the callback is finished, the execution of the main continues
}

main() {
    attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
    attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
    
    // do my complex, long, very nested stuff
    // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
    
    nit();  // disable interruption (interruptions signals will wait for further enable)
    // do some sensitive stuff, that must not be paused
    eit(); // reenable interruption
}
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ạn

Hiện tại, tôi đã triển khai các chủ đề như vậy trên đầu trang của

def myfunction():
   if mythread_exit:  return
   do_some_stuff()
   if mythread_exit:  return
   yet_more_stuff()
   if mythread_exit:  return
   if some_condition():
       if mythread_exit:  return
   	do_an_action():
       if mythread_exit:  return
   and_yet()
   if mythread_exit:  return
4 bằng cách sử dụng
# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
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ốn

Trong 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

Gửi tín hiệu tiêu diệt đến chuỗi python

@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

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

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,

chúng tôi đã tạo một biến cờ mà chúng tôi cần chia sẻ giữa các chuỗi, Nó có thể được chia sẻ bằng cách sử dụng biến toàn cục (không được khuyến nghị) hoặc sử dụng con trỏ/tham chiếu hoặc sử dụng biến miễn phí

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

Đã 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

Làm thế nào để một người gọi

void mycallback() {
    // do something, once the callback is finished, the execution of the main continues
}

main() {
    attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
    attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
    
    // do my complex, long, very nested stuff
    // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
    
    nit();  // disable interruption (interruptions signals will wait for further enable)
    // do some sensitive stuff, that must not be paused
    eit(); // reenable interruption
}
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?

Gửi tín hiệu tiêu diệt đến chuỗi python
Steven D'Aprano

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

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ớ
Tôi thừa nhận rằng tôi chưa nói rõ ràng về sự khác biệt tinh tế giữa đề xuất của tôi (dừng luồng bằng cách gửi cho chúng ngoại lệ) và điều không được khuyến khích bởi các phương pháp hay nhất (giết luồng). Theo cách thứ nhất, bạn để luồng xử lý ngoại lệ theo cách nó muốn và thoát ra theo cách có kiểm soát; . Theo mình thấy thì các good practice hiện nay không khuyến khích cách 2 vì hệ lụy khó kiểm soát của nó (và mình hoàn toàn tán thành). Nhưng nó không nói gì về cách đầu tiên vì nó chưa thực sự là một lựa chọn
Vì vậy, những gì tôi đề xuất thực sự không trái ngược với các thông lệ tốt (theo ý kiến ​​​​của tôi). Chỉ có điều tôi ý thức được rằng nó có thể bị coi là trái ngược

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, nên chúng ta phải cho phép mã Python đa luồng làm điều tương tự

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…

Tôi không hiểu ý của bạn khi nói "con trỏ/tham chiếu" và "biến tự do" ở đây

đơn giản thế này

  • chia sẻ cờ bằng freevar
def main():
	flag = 0
	def mytask():
		nonlocal flag  # this is only needed if 'flag' is assigned in this scope
		sleep(1)
		print(flag)  
		# 'flag' is somehow detached from the scope of 'main' so that it can be modified 
		# in 'main', or 'main' can return and the modifications will be visible on the 'flag' 
		# variable in 'mytask'
	thread = threading.Thread(target=mytask)
	thread.start()
	flag = 1

# outputs '1'
  • chia sẻ với một tham chiếu/con trỏ
def main():
	flag = [0]
	def mytask(flag=flag):
		sleep(1)
		print(flag[0])  
		# 'flag' is a reference to a list, so any modification to its content in 'main' 
		# will be visible from 'mytask'
	thread = threading.Thread(target=mytask)
	thread.start()
	flag[0] = 1

# outputs '1'

Làm thế nào để một người gọi

void mycallback() {
    // do something, once the callback is finished, the execution of the main continues
}

main() {
    attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
    attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
    
    // do my complex, long, very nested stuff
    // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
    
    nit();  // disable interruption (interruptions signals will wait for further enable)
    // do some sensitive stuff, that must not be paused
    eit(); // reenable interruption
}
5 từ API C mà không cần viết mã C?

Đ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

Điều gì xảy ra nếu hai hoặc nhiều chủ đề tự đăng ký để nhận SIGEXC?

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)

Gửi tín hiệu tiêu diệt đến chuỗi python
jimy byerley

Tôi nghĩ rằng các luồng có thể bị gián đoạn sẽ vẫn thú vị, bởi vì nó sẽ cho phép yêu cầu luồng xử lý ngoại lệ hoặc thoát “ngay khi nó an toàn” (có nghĩa là ngay khi luồng điều khiển nằm trong tay con trăn)

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

Nếu một trình xử lý tín hiệu đưa ra một ngoại lệ, thì ngoại lệ đó sẽ được truyền đến luồng chính và có thể được đưa ra sau bất kỳ lệnh nào. Đáng chú ý nhất, a có thể xuất hiện tại bất kỳ thời điểm nào trong khi thực hiện. Hầu hết mã Python, bao gồm cả thư viện tiêu chuẩn, không thể được tạo ra mạnh mẽ để chống lại điều này và do đó (hoặc bất kỳ ngoại lệ nào khác do trình xử lý tín hiệu) trong một số trường hợp hiếm hoi có thể đặt chương trình vào trạng thái không mong muốn

@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
Cuộc gọi lại cấp C chỉ nhận tín hiệu và lưu trữ ở đâu đó để trình thông dịch python có thể tìm thấy nó khi tiếp tục (ví dụ: ở trạng thái ngoại lệ). Sau đó, cuộc gọi lại C thoát mà không thay đổi bất kỳ thứ gì khác sang trạng thái python không xác định
Sau đó, khi trình thông dịch python hoạt động trở lại và kết thúc hoạt động bị tạm dừng, nó sẽ tìm thấy trạng thái ngoại lệ do tín hiệu đặt. sau đó nó có thể thực hiện các cuộc gọi lại tín hiệu và sau đó đưa ra một ngoại lệ nếu cần

(hoặc bất kỳ ngoại lệ nào khác do bộ xử lý tín hiệu) trong một số trường hợp hiếm hoi có thể đặt chương trình ở trạng thái không mong muốn

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
Cho đến bây giờ, để đạt được điều này, không có tùy chọn nào khác ngoài việc đặt lệnh gọi lại cho toàn bộ chương trình python để tắt nó. Sau đó, sau trình tự quan trọng để đăng ký lại cuộc gọi lại ban đầu. Vì vậy, điều này là không thuận tiện
Với API ngắt mà tôi đang đề xuất và trong ví dụ từ tài liệu, chúng ta có thể thực hiện một số thao tác như sau để đảm bảo rằng bất kỳ ngắt nào xảy ra trong quy trình

class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
0 đều được xử lý sau đó

class SpamContext:
    def __init__(self):
        self.lock = threading.Lock()

    def __enter__(self):
        # If KeyboardInterrupt occurs here, everything is fine
        with currentthread().nointerrupts():
            # if SIGINT is received here, it will occur after the 'with' statment
            self.lock.acquire()
        # KeyboardInterrupt could occur just before the function returns

    def __exit__(self, exc_type, exc_val, exc_tb):
        with currentthread().nointerrupts():
            ...
            self.lock.release()

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

class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
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ố
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
2,
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
0,
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
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ụ

try:
   do_stuff()  # <-- if an exception is raised here, the finally block will clean things up
finally:
   # <-- if an exception is raised here, whoops, no cleanup
   cleanup()

Và đặt một

class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
5 bên trong khối
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
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
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
5

Hoặc xem xét

# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
0

Giả 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

class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
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
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
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

void mycallback() {
    // do something, once the callback is finished, the execution of the main continues
}

main() {
    attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
    attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
    
    // do my complex, long, very nested stuff
    // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
    
    nit();  // disable interruption (interruptions signals will wait for further enable)
    // do some sensitive stuff, that must not be paused
    eit(); // reenable interruption
}
7, bởi vì hầu hết các chương trình thoát sau
void mycallback() {
    // do something, once the callback is finished, the execution of the main continues
}

main() {
    attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
    attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
    
    // do my complex, long, very nested stuff
    // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
    
    nit();  // disable interruption (interruptions signals will wait for further enable)
    // do some sensitive stuff, that must not be paused
    eit(); // reenable interruption
}
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

void mycallback() {
    // do something, once the callback is finished, the execution of the main continues
}

main() {
    attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
    attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
    
    // do my complex, long, very nested stuff
    // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
    
    nit();  // disable interruption (interruptions signals will wait for further enable)
    // do some sensitive stuff, that must not be paused
    eit(); // reenable interruption
}
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…
Vì vậy, việc xử lý

# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
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
Gửi tín hiệu tiêu diệt đến chuỗi python

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%
Thế còn việc tìm cách khiến

# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
9 trở nên đáng tin cậy 100% thì sao?

Trước hết, trường hợp này

# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
1

khô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

  • def myfunction():
        with currenthread().nointerrupts():
            # .. do critical stuff here that shall not be interrupted
        # .. do something, very long, very complex, and very nested, that can be interrupted
    mythread = thread(myfunction)
    
    # .. do something
    if whatever_plan_change:
        mythread.terminate()  # alias to   mythread.interrupt(SystemExit)
        # also possible with any exception
        mythread.interrupt(MyCustomException('time to react, thread!'))  
    result = mythread.wait()
    
    6 mã opcode có nghĩa là “Không có gián đoạn nào nữa”

    Nó vô hiệu hóa việc kiểm tra các ngắt cho các hướng dẫn hiện tại và sau

  • def myfunction():
        with currenthread().nointerrupts():
            # .. do critical stuff here that shall not be interrupted
        # .. do something, very long, very complex, and very nested, that can be interrupted
    mythread = thread(myfunction)
    
    # .. do something
    if whatever_plan_change:
        mythread.terminate()  # alias to   mythread.interrupt(SystemExit)
        # also possible with any exception
        mythread.interrupt(MyCustomException('time to react, thread!'))  
    result = mythread.wait()
    
    7 opcode đại diện cho “Thử kích hoạt ngắt”

    Nó cho phép kiểm tra các ngắt cho lệnh hiện tại và lệnh sau. Nhưng nó chỉ làm như vậy nếu cờ luồng cho phép gián đoạn được bật. nếu không nó là một no-op

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

class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
6 và gọi tới ____5_______0 và
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
4 trong câu lệnh
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
1. Và bất kỳ thao tác GC nào như các phương thức
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
2 cũng vô hiệu hóa các ngắt theo cách mà các mã lệnh đó thực hiện

Các ví dụ sau được sửa đổi bytecode của python 3. 8

  • Đối với mệnh đề

    class Thread:
        def nointerrupts(self) -> context manager
            ''' 
            return a context manager that disable interruptions as long 
            as it is openned, all received exceptions will be raised on the 
            context exit 
            '''
    
        def interrupt(self, exception):
            ''' 
            send an exception to interrupt the thread, this can be done 
            from any thread. if the thread has disabled interrupts, the send 
            exceptions will be stacked in an ExceptionGroup and raised on enable 
            '''
            
        # that method to join a thread an check for its possible errors
        def wait(self, timeout) -> result:
            ''' 
            join the thread. if the thread exitted with a return value, 
            it is returned, if the thread exitted with an exception, it 
            is propagated here 
            '''
        
        # maybe also that one, much more similar to the microship's attachInterrupt()
        def attach(self, exception_type, callback):
            ''' 
            when an exception is received by a thread, it is paused and 
            the callback attached to the exception is run. if this callback 
            raises or there is no callback, the exception is raised in the thread 
            '''
    
    6, có vẻ như thế này (và việc thu được không được bảo vệ)

    # in the real world
    
    def myfunction():
        while not my_thread_exit:
            # .. do something, repeatedly, not nested
    mythread_exit = False
    mythread = thread(myfunction)
    
    # .. do something
    if whatever_plan_change:
        mythread_exit = True
    
    2
    # in the real world
    
    def myfunction():
        while not my_thread_exit:
            # .. do something, repeatedly, not nested
    mythread_exit = False
    mythread = thread(myfunction)
    
    # .. do something
    if whatever_plan_change:
        mythread_exit = True
    
    3
  • đối với mệnh đề

    class Thread:
        def nointerrupts(self) -> context manager
            ''' 
            return a context manager that disable interruptions as long 
            as it is openned, all received exceptions will be raised on the 
            context exit 
            '''
    
        def interrupt(self, exception):
            ''' 
            send an exception to interrupt the thread, this can be done 
            from any thread. if the thread has disabled interrupts, the send 
            exceptions will be stacked in an ExceptionGroup and raised on enable 
            '''
            
        # that method to join a thread an check for its possible errors
        def wait(self, timeout) -> result:
            ''' 
            join the thread. if the thread exitted with a return value, 
            it is returned, if the thread exitted with an exception, it 
            is propagated here 
            '''
        
        # maybe also that one, much more similar to the microship's attachInterrupt()
        def attach(self, exception_type, callback):
            ''' 
            when an exception is received by a thread, it is paused and 
            the callback attached to the exception is run. if this callback 
            raises or there is no callback, the exception is raised in the thread 
            '''
    
    1, có vẻ như thế này

    # in the real world
    
    def myfunction():
        while not my_thread_exit:
            # .. do something, repeatedly, not nested
    mythread_exit = False
    mythread = thread(myfunction)
    
    # .. do something
    if whatever_plan_change:
        mythread_exit = True
    
    4_______2_______5

Những ví dụ đó sẽ khắc phục sự cố của

void mycallback() {
    // do something, once the callback is finished, the execution of the main continues
}

main() {
    attachInterrupt(5, mycallback);  // declare the callback for execution in case of change on external pin 3
    attachInterrupt(CLOCK1, myothercallback);   // declare the callback for execution by an internal flag of the microship
    
    // do my complex, long, very nested stuff
    // if an interrupt is triggered, the main is paused, the corresponding callback is called, and then the main is resumed
    
    nit();  // disable interruption (interruptions signals will wait for further enable)
    // do some sensitive stuff, that must not be paused
    eit(); // reenable interruption
}
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
# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
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
# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
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
def main():
	flag = 0
	def mytask():
		nonlocal flag  # this is only needed if 'flag' is assigned in this scope
		sleep(1)
		print(flag)  
		# 'flag' is somehow detached from the scope of 'main' so that it can be modified 
		# in 'main', or 'main' can return and the modifications will be visible on the 'flag' 
		# variable in 'mytask'
	thread = threading.Thread(target=mytask)
	thread.start()
	flag = 1

# outputs '1'
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
# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
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
def main():
	flag = 0
	def mytask():
		nonlocal flag  # this is only needed if 'flag' is assigned in this scope
		sleep(1)
		print(flag)  
		# 'flag' is somehow detached from the scope of 'main' so that it can be modified 
		# in 'main', or 'main' can return and the modifications will be visible on the 'flag' 
		# variable in 'mytask'
	thread = threading.Thread(target=mytask)
	thread.start()
	flag = 1

# outputs '1'
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

  • def main():
    	flag = [0]
    	def mytask(flag=flag):
    		sleep(1)
    		print(flag[0])  
    		# 'flag' is a reference to a list, so any modification to its content in 'main' 
    		# will be visible from 'mytask'
    	thread = threading.Thread(target=mytask)
    	thread.start()
    	flag[0] = 1
    
    # outputs '1'
    
    2 vô hiệu hóa cờ cho phép opcode
    def myfunction():
        with currenthread().nointerrupts():
            # .. do critical stuff here that shall not be interrupted
        # .. do something, very long, very complex, and very nested, that can be interrupted
    mythread = thread(myfunction)
    
    # .. do something
    if whatever_plan_change:
        mythread.terminate()  # alias to   mythread.interrupt(SystemExit)
        # also possible with any exception
        mythread.interrupt(MyCustomException('time to react, thread!'))  
    result = mythread.wait()
    
    7 kích hoạt lại các ngắt, trong thời gian của trình quản lý bối cảnh
  • def main():
    	flag = [0]
    	def mytask(flag=flag):
    		sleep(1)
    		print(flag[0])  
    		# 'flag' is a reference to a list, so any modification to its content in 'main' 
    		# will be visible from 'mytask'
    	thread = threading.Thread(target=mytask)
    	thread.start()
    	flag[0] = 1
    
    # outputs '1'
    
    3 bật cờ sau, trong suốt thời gian của trình quản lý ngữ cảnh

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

# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
6
# in the real world

def myfunction():
    while not my_thread_exit:
        # .. do something, repeatedly, not nested
mythread_exit = False
mythread = thread(myfunction)

# .. do something
if whatever_plan_change:
    mythread_exit = True
7

Tôi không biết opcodes cho câu lệnh

class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
1 và
class Thread:
    def nointerrupts(self) -> context manager
        ''' 
        return a context manager that disable interruptions as long 
        as it is openned, all received exceptions will be raised on the 
        context exit 
        '''

    def interrupt(self, exception):
        ''' 
        send an exception to interrupt the thread, this can be done 
        from any thread. if the thread has disabled interrupts, the send 
        exceptions will be stacked in an ExceptionGroup and raised on enable 
        '''
        
    # that method to join a thread an check for its possible errors
    def wait(self, timeout) -> result:
        ''' 
        join the thread. if the thread exitted with a return value, 
        it is returned, if the thread exitted with an exception, it 
        is propagated here 
        '''
    
    # maybe also that one, much more similar to the microship's attachInterrupt()
    def attach(self, exception_type, callback):
        ''' 
        when an exception is received by a thread, it is paused and 
        the callback attached to the exception is run. if this callback 
        raises or there is no callback, the exception is raised in the thread 
        '''
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
def main():
	flag = [0]
	def mytask(flag=flag):
		sleep(1)
		print(flag[0])  
		# 'flag' is a reference to a list, so any modification to its content in 'main' 
		# will be visible from 'mytask'
	thread = threading.Thread(target=mytask)
	thread.start()
	flag[0] = 1

# outputs '1'
4 cũng có thể được tích hợp trong các opcodes đó, vì vậy mã byte