【问题标题】:pyGame in a threadpyGame 在一个线程中
【发布时间】:2011-02-27 13:48:49
【问题描述】:

我想将 pyGame 程序用作另一个进程的一部分。使用以下代码,pyGame 似乎没有处理事件;它不响应“q”键,也不绘制窗口的标题栏。如果go() 没有作为线程运行,它工作正常。这是在 OSX 下;我不确定这是否是问题所在。

import pygame, threading, random

def go():
  pygame.init()
  surf = pygame.display.set_mode((640,480))
  pygame.fastevent.init()

  while True:
    e = pygame.fastevent.poll()
    if e.type == pygame.KEYDOWN and e.unicode == 'q':
      return

    surf.set_at((random.randint(0,640), random.randint(0,480)), (255,255,255))
    pygame.display.flip()

t = threading.Thread(target=go)
t.start()
t.join()

【问题讨论】:

  • 您的代码在我的 (linux) 机器上运行良好。
  • 也在我的 linux 机器上运行,似乎是 OSX 特有的问题。
  • 仅供参考,Python 有一个 GIL(全局解释器锁),它可以防止真正的线程并行(即,如果您使用线程,它不会使您的代码更快)。
  • 在 Mac 上遇到同样的问题,如果不在主线程中完成绘图就不会发生。不是真正的解决方案,而是更多的观察是,如果在主线程中调用 display.flip() 而在另一个线程中完成绘图,屏幕将根据需要进行更新。

标签: python macos multithreading pygame


【解决方案1】:

Pygame 不是线程安全的,事件循环需要在主线程上运行!否则可能会出现您描述的问题。

一种解决方案是从主线程调用pygame.mainloop()

但是,也许您正在使用也需要从主线程运行的其他模块。在这种情况下,有一个 pythonic 解决方案。您可以使用参数运行 pygame mainloop。这个参数的意思是:只运行主循环几秒钟。因此,您可以做的是创建一个运行 mainloop 0.1 秒的生成器,您可以从主线程定期调用该生成器。例如:

def continue_pygame_loop():
    pygame.mainloop(0.1)
    yield

然后从主线程定期调用continue_pygame_loop()

Tkinter 也遇到了同样的问题,但无法指定 runloop() 超时。对我来说,这就是 pygame 很棒的原因!

【讨论】:

  • 据我所知,pygame 没有mainloop() 功能。 (我使用的是 v1.9.2),所以不清楚你在说什么。当您将其与 tkinter 混淆时(哪个)?
【解决方案2】:

最好在主线程中进行事件处理和图形处理。有些环境真的不喜欢您尝试从其他线程进行渲染,而有些环境也不喜欢您尝试从它们中耗尽事件队列。

它甚至不可能做你希望做的事情,因为你正在运行的进程很可能对谁拥有消息队列和你正在渲染的窗口有自己的想法。

【讨论】:

    【解决方案3】:

    可能很长,但请确保在进入线程之前不要导入 Pygame。问题很可能是您在一个线程上导入 Pygame,然后在另一个线程上使用它。但是,在多个线程上导入可能会有其他问题。特别是,请确保在启动 Pygame 线程后等待它完成导入,然后再执行任何可能导致 Python 进程关闭的操作,否则您可能会遇到死锁。

    【讨论】:

    • 这对我在 Windows 上使用 Python 3.3.5 确实有效
    【解决方案4】:

    并不是一个真正的答案,只是确认这个问题确实发生在 Mac OS X 上而不是 Linux 上。我在 Ubuntu 上开发了我的程序,这种代码在那里运行良好,但是当我尝试在 Mac 上运行它时,它失败了,正如你所描述的那样。在主线程中运行绘图代码可以正常工作。

    如果这确实是线程模块或 pygame(在 Mac 上)的限制,那么我想唯一的方法是重组你的程序,以便所有绘图都由主线程处理。或者,提交错误报告,看看会发生什么。

    编辑:另一个更好的选择是使用multiprocessing 模块。您可以生成一个单独的 python 进程,仅用于渲染到屏幕。然后进程可以相互传递信息。我正在考虑自己使用它。

    【讨论】:

      【解决方案5】:

      通过在正在启动的线程中而不是在顶部调用 pygame.init() 为我解决了这个问题。例如,我的类初始化了一个线程并定义了一个作为主循环的函数:

      class Thing():
          # other class stuff
          def __init__(self):
              # other init stuff
              self.thread = threading.Thread(target=self.gogo)
              self.thread.start()
      
          def gogo(self):
              print('gogo')
              pg.init()  # <-THIS!
              self.screen = pg.display.set_mode(SIZE)
              while self.get_working():
                  for event in pg.event.get():
                      if event.type == pg.QUIT:
                          # etc
      

      之前,屏幕和初始化是在开始时完成的,它完全可以在 Ubuntu 18.04 上运行,只是不能在 windows 10 机器上运行。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-01
        • 2020-10-24
        • 2021-06-04
        • 2020-12-14
        • 2011-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多