V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
binxin
V2EX  ›  Python

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

  •  
  •   binxin · 2017-08-27 22:11:06 +08:00 · 2503 次点击
    这是一个创建于 2636 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    问题大概是这样的:在用 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. 症状就是,点击之后没有任何反馈,然后一瞬间走两步。

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

    2 条回复    2017-08-28 13:07:59 +08:00
    NoAnyLove
        1
    NoAnyLove  
       2017-08-28 00:25:06 +08:00
    原因很简单,你的`queue_handler`都是通过 Tkinter 组件的`after`方法调用的,这个方法调用`queue_handler`方法是在主线程( UI 线程中),也就是说`iterative_deepening`阻塞了 UI 线程,所以卡住了
    binxin
        2
    binxin  
    OP
       2017-08-28 13:07:59 +08:00
    @NoAnyLove
    谢谢!我试试看。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5361 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 08:21 · PVG 16:21 · LAX 00:21 · JFK 03:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.