Python3 中,如何用一个队列进行先后读取(非阻塞)

2017-08-27 22:11:06 +08:00
 binxin

目测我的标题取错了,但是我实在不知道怎么用术语描述。

问题大概是这样的:在用 python 写一个五子棋 AI,那么就需要玩家下一步,电脑思考,电脑下一步。现在电脑思考大概需要 2 秒的时间。

代码大概是这样的:

que_game2ui = Queue(maxsize=2)
que_ui2game = Queue(maxsize=2)

class GUI:
    def __init__(self, ui):
    	# self.bg = ui 的一个 canvas
    	pass

    def on_click(self, event):
	    col = (event.x + GUICONF["half_gap"]) // GUICONF["gap"] - 1
	    row = (event.y + GUICONF["half_gap"]) // GUICONF["gap"] - 1
	    que_ui2game.put({
	        "option": "move",
	        "loc": (row, col),
	    })


    def queue_handler(self):
    try:
        task = que_game2ui.get(block=False)
        # 更新界面
        self.bg.after(10, self.queue_handler)
    except queue.Empty:
        self.bg.after(10, self.queue_handler)


class GAME(Gomokuy):
    def __init__(self, _gui)
    	# Gomokuy 是自己写的 AI 的类
    	# self.gui = _gui
    	pass

    def moving(self, row, col):
        if self.winner:
            print("游戏结束")
            return

        ret = self.move((row, col))
        if ret:
            que_game2ui.put({
                "game": self,
                "info": "move"}, block=False)

            pos2 = self.iterative_deepening(self.difficulty)	# 这个函数很耗时
            self.move(pos2)
            que_game2ui.put({
                "game": self,
                "info": "move"}, block=False)
        else:
            que_game2ui.put({
                "game": self,
                "info": "Game Over"})


    def queue_handler(self):
        try:
            task = que_ui2game.get(block=False)
            if task["option"] == "move":
                row, col = task["loc"]
                self.moving(row, col)
            self.gui.after(10, self.queue_handler)
        except queue.Empty:
            self.gui.after(10, self.queue_handler)

if __name__ == '__main__':
    window = Tk()
    gui = GUI(window)
    t1 = threading.Thread(target=GAME, args=(window,))
    t1.setDaemon = True
    t1.start()
    window.mainloop()

问题在于 GAME.moving 这里,分别有两次 put 操作,我设想的情况应该是:

  1. 第一次 put 之后,GAME 线程继续计算下一步 AI 应该怎么走( iterative_deepening )。
  2. GUI.queue_handler 拿到第一次 put 的内容之后,立即把玩家下的这一步绘制出来。
  3. n 秒后,GAME 算出来了,于是第二次 put,然后 GUI 再绘制,把电脑的落子也绘制出来。

但是根据实际症状,以及添加的调试信息来看,结果实际是这样的:

  1. 第一次 put 之后,GAME 算,算完之后才第二次 put。
  2. 第二次 put 之后,GUI 一次性把两次信息都拿出来(这个还看不出来,似乎 GUI 每次都只取到了最后一次 put,也就是走了两步之后的结果)绘制。
  3. 症状就是,点击之后没有任何反馈,然后一瞬间走两步。

感觉没有达到拆分线程的目的,请问代码思路是哪里有问题呢?

2446 次点击
所在节点    Python
2 条回复
NoAnyLove
2017-08-28 00:25:06 +08:00
原因很简单,你的`queue_handler`都是通过 Tkinter 组件的`after`方法调用的,这个方法调用`queue_handler`方法是在主线程( UI 线程中),也就是说`iterative_deepening`阻塞了 UI 线程,所以卡住了
binxin
2017-08-28 13:07:59 +08:00
@NoAnyLove
谢谢!我试试看。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/386204

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX