【问题标题】:python: writing to a global variable from separate thread creates a copypython:从单独的线程写入全局变量会创建一个副本
【发布时间】:2017-01-02 12:25:14
【问题描述】:

我正在尝试创建一个全局状态变量,它是在回调方法(事件处理程序)中编写的。 但是,回调会在另一个内存位置创建一个副本(深),(当然)其他方法看不到该副本。 情况是这样的

class Server:
  def __init__(self):
    self.callbacks=[]
    #create a web server instance to listen to requests
    self.app=Flask("test")

  def add_callback(self, func):
    self.calbacks.append(func)
    self.app.add_url_rule("/test", "test", self.handle_http_request)

  def handle_http_request(self):
    content = request.get_json(silent=True)
    for ca in self.callbacks:
      ca(content)

  def start_server(self):
    #some stuff starting flask here...


class SomeModule:
  def __init__(self):
    self.ws=Server()
    self.ws.add_callback(self.callback)
    self.callback_called=False

  def callback(self, content):
    print "callback executing---"
    print "var addr before callback assign: "+str(hex(id(self.callback_called)))
    self.callback_called=True
    print "var addr after callback assign: "+str(hex(id(self.callback_called)))

  def start(self):
    self.ws.start()
    #send a request to the server using the request library, which invokes all the trigger
    #check the state variable:
    print "var addr before check: "+str(hex(id(self.callback_called)))
    if (not self.callback_called):
      raise Exception("error...")

if __name__ == '__main__':
  sm=SomeModule()
  sm.start()

那么输出是:

回调执行--- 回调分配前的 var addr:0x927910 回调分配前的 var addr:0x927930 检查前的 var addr:0x927910

谁能给我建议一种方法来避免这种情况? 在 c++ 中,它清楚地知道如何访问指针和互斥锁。然而,在这里,我没有设法找到任何方法来对变量进行安全写入......

提前非常感谢!

【问题讨论】:

    标签: python multithreading callback


    【解决方案1】:

    由于您在标签中使用多线程,而不是多处理,我仍会继续发布此答案。可能对某些人有所帮助。

    有些对象是immutable,照你说的复制。其他变量,例如 dictionaries 往往不会被函数或威胁操纵(不确定线程​​是否仅适用于某些情况)。

    例如,如果您将dict 作为参数传递给线程,则可以操纵该变量,这会影响变量的原始版本。

    但是,这样做是有风险的。可能存在更新冲突、访问冲突,并且通常很难跟踪发生的事情。

    但这里有一个如何将dict 传递到线程并呈现更改的示例。这很粗略,但给了你一个可行的例子。

    from threading import *
    
    class server(Thread):
        def __init__(self, o):
            self.o = o
            Thread.__init__(self)
            self.start()
    
        def run(self):
            for i in range(3):
                self.o['test'] = i
    
    test_var = {'test' : 0}
    server(test_var)
    
    while len(enumerate()) > 1: # Stupid and oversimplified wait for threads to end.
        pass
    
    print(test_var)
    

    【讨论】:

    • 非常感谢。当我用字典替换变量 self.callback_call 时,内存检查确实显示了相同的位置。但是,即使变量设置为 eg.self.callback_called['done'] = True,在检查期间它也会再次被视为 False。感觉好像我错过了一些明显的东西...... :(
    • @mojovski 您是否要快速打印变量?因为在我的测试中,我让它完美地工作。你能把你的试用代码粘贴到gist.github.com吗?
    【解决方案2】:

    问题实际上出在其他地方。 :(原因是,Flask(Web服务器)是在一个单独的线程中启动的,并且由于某种原因复制了一些实例。我没有进一步挖掘,只是避免使用Flask并切换到cherrypy并以非-阻塞模式。 Thinkgs 开始按预期在那里工作。

    @torxed,非常感谢您的帮助。它确实为我指明了正确的方向!

    【讨论】:

    • 不客气 :) 不幸的是,这个问题没有用Flask 标记,因为我在你的代码中忽略了这一点 :) 很高兴这一切都解决了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多