Numergy / signalslot

Simple signal slot implementation in Python
http://signalslot.rtfd.org
MIT License
47 stars 14 forks source link

Emitting a signal from a thread ? #10

Closed Overdrivr closed 9 years ago

Overdrivr commented 9 years ago

Is it ok (=thread-safe) to do that ?

I suppose it is only thread safe if the called func is also thread-safe can you confirm ?

Thank you PS : Great little module, good job ;)

jpic commented 9 years ago

Question is too vague, please elaborate.

Overdrivr commented 9 years ago

I have a program which decodes serial data in a thread. When a specific frame is detected in the serial flow, I call Signal.emit (from this thread), then data is plotted, etc. Now sometimes this thread won't return and hangs on exit, and I isolated the line causing this issue to be this Signal.emit line.

I am not saying this is Signal.emit fault, instead I am starting to think what I am doing is very bad practice to emit signals from a thread, because the function connected the signal might not be thread safe.

So, to sum up: When calling emit, are the connected functions called immediatly (= in the current thread) or is the emit event put on a event list and called on the main thread later ?

jpic commented 9 years ago

Ok, is there any way we can reproduce this ?

Overdrivr commented 9 years ago

I cannot reproduce it with this simple code:

from signalslot import Signal
import threading
import random
import queue

def callback(count,**kwargs):
    print("Drawn number3 "+str(count)+" times.")

class Tester():
    def __init__(self):
        self.queue = queue.Queue()

        self.input_thread_running = True
        self.input_thread = threading.Thread(target=self.add)

        self.output_thread_running = True
        self.output_thread = threading.Thread(target=self.remove)

        self.count = 0
        self.signal = Signal(args=['count'])
        self.signal.connect(callback)

        self.output_thread.start()
        self.input_thread.start()

    def add(self):
        while self.input_thread_running:
            self.queue.put(random.randint(0,10))
        print("Input thread finished.")

    def remove(self):
        while self.output_thread_running:
            i = self.queue.get()
            if i == 3:
                self.count += 1
                #print("Drawn 3")
                self.signal.emit(count=self.count)
                #print("Signal emitted")
        print("Output thread finished.")

    def stop(self):
        self.input_thread_running = False
        self.output_thread_running = False
        self.input_thread.join()
        self.output_thread.join()

if __name__ == '__main__':
    t = Tester()
    while t.count < 20:
        pass
    t.stop()

I looked at your library code and I realize my question is silly. Calling emit on a signal connected to non thread-safe function is of course non thread-safe. No magic there

In my code, the emitted signals called matplotlib drawing functions, which I just found out must not be called from secondary threads. So I guess that pretty much answer my question, the thread hanging issue must have come from there.

I'll just close it if that's fine with you, regards

jpic commented 9 years ago

Well done @Overdrivr B)

Thanks a heap for sharing your code !