Sorry for taking so long to reply, I wanted to get some weakref experience before commenting as I've never encountered it before.
It seems weakref itself is not enough to keep threads from causing crashes, and it's my understanding that all it does is to allow objects to be garbage collected when the only references to said object are weak. It shouldn't have any affect on threading, but please correct me if I'm wrong. Here is an example similar to what I ended up with. http://code.activestate.com/recipes/577980/ Now, about your integration with threads and Qt, this may work, although I honestly didn't look through it too carefully because as I started having a signal class external to Qt, I've found other cases where this pattern is useful - i.e. in separating logic and ui, but also in lower level functionality unrelated to ui's - and are still investigating a way to decouple it from Qt altogether. As far as CustomSignal, threading and Qt goes, I'm a few days in with trying to use both pyqtSignal and CustomSignal in tandem, pyqtSignal where I need interprocess communication (or really, where things crash) and CustomSignal everywhere else. As they share interface I haven't yet encountered a situation where it creates confusion. About ThreadedServer. As far as I can tell, it just means that it threads each incoming task in a separate thread. The alternative would be ThreadedPoolServer which can do some queue-magic, but ultimately RPyC isn't an event-based networking engine (like say Twisted<https://twistedmatrix.com/trac/>). But I'm no expert and could be wrong. On 22 January 2014 08:22, Justin Israel <[email protected]> wrote: > Ah! Things are a lot clearer now. Thanks for explaining that. I get what > you meant, and I actually see reasons why it would cause a crash :-) > > I can't speak to specifically about rpyc, since I have never used it, but > I can comment from a general viewpoint about some things: > > In your custom signals class, when you perform the connect(), you are > storing the function reference. That can be just fine for a general > function, but for a method and even worse for a method of a QObject, you > will end up with a problem. Storing just the method reference is not enough > to prevent the object it is bound to from being garbage collected. So it is > very possible in situations where you allow an object to be garbage > collected, and there is no mechanism in there to disconnect the signal. it > would just end up potentially calling on a dead object. Qt Signal/Slot > automatically disconnects when QObjects are deleted. > > Another thing is that there is no mechanism in your custom signals for > when you are emitting from another thread. In Qt, if you emit from one > thread to a sender in another thread, you have available the QueuedConnection > type, which doesn't run the slot directly, but rather places it into the > receivers event loop to be run in that thread. So your version would need > to be calling those functions on thread safe functions/methods only. > > I did some work extending a concept on ActiveState about weakmethods (as > in weakrefs): https://gist.github.com/justinfx/6183367 > It has code for doing weakmethods, as well as a system for doing arbitrary > callbacks from another thread into the main threads event loops (kind of > like what you are missing with QueuedConnections). Maybe that code would > help in extending your custom signal design? > > Like I said, I don't really know much about the rpyc library, but one > thing that stands out in the example is that you are staring a threaded > server, from within another thread. Is that necessary? Probably unrelated, > and I don't know if a specific crash in this example involves that library > or not. > > > > > On Wed, Jan 22, 2014 at 8:45 PM, Marcus Ottosson > <[email protected]>wrote: > >> I think maybe I misunderstood you previously. I thought we *were* talking >>> about using signals to pass data between threads? But you are asking if I >>> have done it without signals? >>> >> >> Yes, I think this is where we may have a misunderstanding. It probably >> stems for my ambiguity in use of the words *pyqtSignal* and the custom >> implementation *Signal *(as in the above article). >> >> What I am trying to convey, is that using *pyqtSignal* works with >> threads (as far as I can tell), but *Signal *(which I'll refer to as >> *CustomSignal *from now on) in an identical scenario may not. >> >> In the above example, I provided both pyqtSignal() and CustomSignal() as >> class attributes for the thread (CustomClass being temporarily commented >> out). I was trying to illustrate that taking out pyqtSignal for >> CustomSignal would cause a crash, but I should have been more explicit, so >> apologies for this. >> >> Now I certainly see why wanted to get to the bottom of this! :) If >> someone made the claim that using Qt's own QThreads with pyqtSignal would >> be cause for crashes, I'd be doing the same thing. >> >> In any case, I may still be completely off in my statement regarding >> passing data across threads causing crashes, so I'm glad we're still on the >> topic. In the three examples you listed, Justin, thread-safety is already >> inherent. What I would love to get a hold on however, is whether it is >> considered safe to pass data from one thread to another, or to simply >> trigger methods from one thread *to* another, by using a class as simple >> as my CustomSignal implementation that has no regard for locking of >> resources during its run. >> >> I'm still having crashes and occasional hang-ups in my original program, >> and made a slightly longer version of in a minimal example, but did not >> succeed in reproducing the crash. Posting as is, just in case, and will >> once again return once I know more. >> >> >> import threading >> >> >> from PyQt5.QtWidgets import * >> from PyQt5.QtCore import * >> >> >> PORT = 18000 >> >> >> >> >> class CustomSignal: >> >> >> def __init__(self): >> self.__subscribers = [] >> >> def emit(self, *args, **kwargs): >> for subs in self.__subscribers: >> subs(*args, **kwargs) >> >> >> def connect(self, func): >> self.__subscribers.append(func) >> >> def disconnect(self, func): >> try: >> self.__subscribers.remove(func) >> except ValueError: >> print('Warning: function %s not removed ' >> 'from signal %s'%(func,self)) >> >> >> >> >> class Window(QWidget): >> >> >> rpc_show = pyqtSignal() # Causes no crash >> # rpc_show = CustomSignal() # Causes crash >> >> >> >> def __init__(self, parent=None): >> super(Window, self).__init__(parent) >> >> >> >> self.__rpc_server = None >> >> >> self.rpc_show.connect(self.restore) >> >> >> def start_rpc(self): >> from rpyc.utils.server import ThreadedServer >> from rpyc.core import SlaveService >> >> >> if self.__rpc_server: >> self.__rpc_server.close() >> self.__rpc_server = None >> >> >> class Service(SlaveService): >> def exposed_show(self): >> self.show_signal.emit() >> >> >> Service.show_signal = self.rpc_show >> server = ThreadedServer(Service, port=PORT, reuse_addr=True) >> >> >> self.__rpc_server = server >> >> >> def thread(): >> self.__rpc_server.start() >> >> >> print("Running RPC server") >> thread = threading.Thread(target=thread, name="rpc") >> thread.daemon = True >> thread.start() >> >> >> def restore(self): >> self.activateWindow() >> self.showNormal() >> >> >> >> >> def start_application(): >> >> import sys >> app = QApplication(sys.argv) >> >> >> win = Window() >> win.show() >> >> >> >> win.start_rpc() >> >> >> sys.exit(app.exec_()) >> >> >> >> >> def request_application(): >> print("Requesting a new Window") >> >> import socket >> import rpyc >> >> >> try: >> proxy = rpyc.connect('localhost', PORT) >> proxy.root.show() >> print("Restored existing instance of Window") >> >> >> except socket.error: >> print("Running new instance of Window") >> start_application() >> >> >> >> >> if __name__ == '__main__': >> request_application() >> >> -- >> You received this message because you are subscribed to the Google Groups >> "Python Programming for Autodesk Maya" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to [email protected]. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/python_inside_maya/95bdc1ec-09cd-4086-baee-21715533db5b%40googlegroups.com >> . >> >> For more options, visit https://groups.google.com/groups/opt_out. >> > > -- > You received this message because you are subscribed to the Google Groups > "Python Programming for Autodesk Maya" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > To view this discussion on the web visit > https://groups.google.com/d/msgid/python_inside_maya/CAPGFgA3%2B_t_FCEOnpLD8k_XMYk1GawfzKuKsQUgmC59y-34SXA%40mail.gmail.com > . > > For more options, visit https://groups.google.com/groups/opt_out. > -- *Marcus Ottosson* [email protected] -- You received this message because you are subscribed to the Google Groups "Python Programming for Autodesk Maya" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/python_inside_maya/CAFRtmOBrhU1Q_Annb%3D%2BfjYQ-kU%2B5fC5nsB3pkbYDkLYpjJDpAQ%40mail.gmail.com. For more options, visit https://groups.google.com/groups/opt_out.
