交叉线程更新观察者模式(非阻塞)

时间:2016-10-26 18:46:41

标签: python python-multithreading

SO的新手,请原谅任何礼节错误(指出是否有!)。

我正在处理在程序主UI线程上运行的脚本。话虽这么说,我需要避免所有阻止调用,以确保用户仍然可以进行交互。我无法访问UI事件循环,因此在我的情况下,任何繁忙的循环解决方案都是不可能的。

我有一个简单的后台线程,它正在与另一个应用程序通信并收集数据,并存储在一个简单的数组中以供使用。每次更新此数据时,我都需要使用数据来修改UI(这必须在主线程中运行)。理想情况下,后台线程每次更新数据时都会发出一个信号,然后在主线程中,侦听器会处理这个并修改UI。繁忙的循环不是一个选项,一切都必须基于异步/事件。

我使用threading.timer(..)在后台继续运行数据收集。但是,由于这是在一个单独的线程中运行,因此需要在外部调用UI操作。

def _pollLoop(self): 
    if (self._isCubeControl):
        self._getNewData()
        #in a perfect world, updateUI() would be here
        self._pollThread = threading.Timer(0.1,self._pollLoop)
        self._pollThread.start()

我需要一种方法让这个pollLoop回调到主线程,这样我就可以更新UI了。我在pollLoop中尝试过直接回调,但回调是在单独的线程中运行导致错误。

寻找一种方法将侦听器附加到数据对象,以便在更改时可以在主线程中运行updateUI()。

感谢您提供的任何帮助!如果这很模糊,请告诉我

更新

根据@ CAB的答案,我现在正试图实施观察员模式。难点在于Observable将在生成的线程中运行,而Observer更新函数必须在主线程中运行。我已经实施了示例chad lung(http://www.giantflyingsaucer.com/blog/?p=5117)。

import threading

class Observable(object):

    def __init__(self):
        self.observers = []

    def register(self, observer):
        if not observer in self.observers:
            self.observers.append(observer)

    def unregister(self, observer):
        if observer in self.observers:
            self.observers.remove(observer)

    def unregister_all(self):
        if self.observers:
             del self.observers[:]

    def update_observers(self, *args, **kwargs):
        for observer in self.observers:
            observer.update(*args, **kwargs)
        thread = threading.Timer(4,self.update_observers).start()

from abc import ABCMeta, abstractmethod

class Observer(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def update(self, *args, **kwargs):
        pass

class myObserver(Observer):  
    def update(self, *args, **kwargs):
        '''update is called in the source thread context'''
        print(str(threading.current_thread()))

observable = Observable()

observer = myObserver()
observable.register(observer)

observable.update_observers('Market Rally', something='Hello World')

我得到的回应是:

<_MainThread(MainThread, started 140735179829248)>
<_Timer(Thread-1, started 123145306509312)>
<_Timer(Thread-2, started 123145310715904)>

显然Observer正在生成的线程中运行而不是main。有人对我有另一种方法吗? :)再一次,我不能有一个繁忙的循环来定期检查值的变化(我希望.. :()这个脚本运行超过UI,我无法访问GUI事件循环,所以一切都需要异步和非阻塞。

1 个答案:

答案 0 :(得分:2)

让我们从http://www.giantflyingsaucer.com/blog/?p=5117开始构建该示例。

from abc import ABCMeta, abstractmethod

class Observer(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def update(self, *args, **kwargs):
        pass

然后,在Observer实现上有责任断开线程。让我们说我们使用一个简单的线程来做到这一点。 (语法可能已关闭,我正在填写此内容并需要捕获总线)。

from observer import Observer
from threading import Thread

class myObserver(Observer):

    def update(self, *args, **kwargs):
        '''update is called in the source thread context'''
        Thread(target=self.handler, args=(self,*args), kwargs=**kwargs).start()

    def handler(self, *args, **kwargs):
       '''handler runs in an independent thread context'''
       pass # do something useful with the args