Python Thread会不断冻结整个程序?

时间:2017-01-04 02:50:26

标签: python-2.7 events thread-safety python-multithreading libtcod

在所有事情之前,我要说我不是在python编程中,因为我在其他语言中。我对寻找其他解决方案感到非常恼火,所以请事先感谢你的帮助。

我喜欢在空闲时间制作Roguelike游戏,所以我尝试了很多方法来使用C ++,C,C#,HTML5等自己的“引擎”实现和我自己的游戏引擎。我从来没有以前在LibTCOD工作过,因为我从来没有能够在C ++中使用它是我最喜欢的编程语言,这是一个我现在不会在C ++线程中讨论的问题。

非常好,LibTCOD看起来很棒,但提及和文档准确性太少,所以我几乎一个人工作。最后几天我做了一个小python包来轻松管理python和windows的LibTCOD功能,并使主游戏代码尽可能小。

我尝试添加的最终实现是将主游戏循环传递给线程,处理每个基本游戏功能(如键盘/鼠标更改和屏幕更新),并通过函数调用运行它。

一切正常......但不是在第一个循环步骤之后,因为它冻结一切并停止工作。

基本上这是有问题的代码:

def ioHandler(l):
    lastx = 0
    lasty = 0
    lastk = None
    c = 0
    noEvent = 0
    casted = False
    while not tcod.console_is_window_closed():
        l.acquire()
        try:
            tcod.sys_check_for_event(tcod.EVENT_KEY_PRESS|tcod.EVENT_MOUSE,key,mouse)
        finally:
            l.release()
        if mouse.lbutton_pressed:
            casted = True
            l.acquire()
            try:
                onClick(mouse, 'left')
            finally:
                l.release()
        if mouse.rbutton_pressed:
            casted = True
            l.acquire()
            try:
                onClick(mouse, 'right')
            finally:
                l.release()           
        if mouse.cx != lastx or mouse.cy != lasty:
            casted = True
            l.acquire()
            try:
                lastx = mouse.cx
                lasty = mouse.cy
                onMouseMove(mouse)
            finally:
                l.release()
        if key != lastk:
            casted = True
            l.acquire()
            try:
                lastk = key
                onKeyPress(key)
            finally:
                l.release()
        if not casted: noEvent += 1
        l.acquire()
        try:
            onTickFrame(c+1)
        finally:
            l.release()

这里使用的大多数变量都是为了更清晰的调试理解目的,(即使它的“干净”功能也冻结了)所以我要将它们放在那里。

从这里调用上面的'def':

def main_loop():
    l = threading.Lock()
    tr = threading.Thread(target=ioHandler, args=(l,))
    #tr.daemon=True
    tr.start()

对于'赛事'系统,我在互联网上找到了这个:

class Event:
    handlers = set()
    def __init__(self):
        self.handlers = set()

    def handle(self, handler):
        self.handlers.add(handler)
        return self

    def unhandle(self, handler):
        try:
            self.handlers.remove(handler)
        except:
            raise ValueError("Handler is not handling this event, so cannot unhandle it.")
        return self

    def fire(self, *args, **kargs):
        for handler in self.handlers:
            handler(*args, **kargs)

    def getHandlerCount(self):
        return len(self.handlers)

    __iadd__ = handle
    __isub__ = unhandle
    __call__ = fire
    __len__  = getHandlerCount

作为一个注释:我正在使用Python 2.7,这是唯一适用于库的版本,真可惜。

我认为事件系统可能是主要问题。再次读取代码,我想我也应该对while条件应用一个锁,因此对整个循环,或者这是不是必要的?锁是否以适当的方式应用?或者我应该使用其他方法使线程有效吗?

提一下,如果主游戏循环在没有线程的主脚本上进行,一切正常,但是当作为线程调用时一切都失败,或者即使它不是一个线程本身,但它从其他任何其他地方被调用包中的函数,所以它不能成为库问题(我认为)。

我不得不说,我只使用Python中的LibTCOD,因为我无法在它上面工作(至少在Windows上)。如果它有帮助,我已经看到python库的代码只是原始C库的'bind',所以理解python代码并不是什么大问题。对于最后一个声明,我认为这也是python线程的问题,或者我错了?如果我能做些什么来修复线程工具,请帮助我!

谢谢大家!我希望我的谈话不会让你感到无聊。

1 个答案:

答案 0 :(得分:0)

你的例子中有足够的遗漏,我无法及时运行,所以我没有一个已知的解决方案,但我确实有一些建议:

  1. 如果同一个帖子将释放获取它的锁,我建议使用RLock() instead of Lock()
  2. l = threading.RLock()

    1. 为了使代码更清晰,更少错误探测,我建议使用锁提供的上下文管理器:
    2. 而不是:

      l.acquire():
      try:
          tcod.sys_check_for_event(
              tcod.EVENT_KEY_PRESS | tcod.EVENT_MOUSE, key, mouse)
      finally:
          l.release()
      

      尝试:

      with l:
          tcod.sys_check_for_event(
              tcod.EVENT_KEY_PRESS | tcod.EVENT_MOUSE, key, mouse)
      
      1. 至于应该锁定什么的问题。如果不了解所有数据结构,这很难回答,但一般来说,任何将在多个线程中使用的东西都应该被锁定。