为什么在 PyGame GUI 事件循环内启动的线程会减慢 GUI 的速度?

问题描述 投票:0回答:1

不久前,我发布了一个有关在 GUI 循环内启动线程的问题,其中命令应与 GUI 事件/绘图循环并行运行。这应该是一个测量循环,应根据用户的决定启动和停止。我从 stackoverflow 得到了答案,并且我已将代码实现到我的应用程序中。它似乎有效,但它减慢了 GUI 元素的绘制和响应速度(当然它不应该,因为它在单独的线程中运行)。它滞后太多,以至于 GUI 变得几乎没有响应。尽管单独线程中的测量循环以正常速度运行。我应该在另一个线程中运行 GUI 的重绘吗?缩短的代码发布在下面。尽管我尝试输入了解应用程序如何工作所必需的所有内容,但某些元素可能会丢失。


#function to execute in separate thread - if measuring puts a random number in a queue 

def takeMeasurements(run, measure, q, sleep_time):
    while run.is_set():
        if measure.is_set():
            print("Measuring...")
            q.put(random())
        else:
            print("Not measuring...")
        sleep(sleep_time)

########pygame code######## 

pygame.init()

#here goes a lot of repetitive GUI code    
#{....}

run=True

MEASURE=False

#event and GUI update loop

while run:
    time_delta = clock.tick(60)/1000.0
    screen.fill((0,0,0))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run=False  
        if event.type== pygame_gui.UI_BUTTON_PRESSED:
            if event.ui_element == button_set_param: 
            #if user pushes button starting measurement
                if stdb_run=="Standby":
                    pass
                elif stdb_run=="Run":
                    if MEASURE==False:
                        #initializing threading, only when MEASURE==False, so it executes only once
                        (threadRun := Event()).set()
                        (threadMeasure := Event()).set()
                        queue = Queue()
                        #start thread with sleep=1 s
                        (t := Thread(target=takeMeasurements, args=[threadRun, threadMeasure, queue, 1])).start()
                    else:
                        #if measurement run second time or more
                        threadMeasure.set()
                    MEASURE=True

        if event.type == pygame_gui.UI_BUTTON_PRESSED:
              #if User pushes stop button and stops the measurement
              if event.ui_element == button_stop:
                    threadMeasure.clear()

    try:
        #if the measuremnt was started, and  queue object exists, if not -> pass
        m=queue.get()
        #displaying a random number in GUI element
        entry_vbridge.set_text(str(m))
    except:
        pass

    #updating GUI
    manager.update(time_delta)
    manager.draw_ui(screen)
    screen.blit(surf, (750,400))
    pygame.display.flip()

#code on closing the app
try:
    threadRun.clear()
    t.join()
except NameError:
    threadRun=None
    t=None

pygame.quit()

我愚蠢地认为 takeMeasurements 函数中的 sleep() 命令不仅在线程中起作用,而且在 GUI 更新循环中以某种方式起作用。有 thread.sleep(),但我不能在函数体中使用它,因为线程没有作为参数传递给函数。

python user-interface python-multithreading pygame-gui
1个回答
0
投票

这里的主要问题似乎是 Python 中不存在“真正的”多线程——至少如果您使用的是常规 Python 实现(即 CPython),则不会。这意味着在任何时刻都只能有一个 Python 级别的线程在运行,因为任何正在运行的线程都需要获取 GIL(全局解释器锁)。换句话说,Python 级线程与 C 级线程有很大不同,并且只对 IO 密集型任务有意义。如果你有一个非 IO 密集型、计算量大的任务,比如“进行测量”,那么它将阻塞其他任务 - 除非你的第一个任务自动且非常定期地释放 GIL,将控制权交还给 Python 主线程,以便其他任务可以运行。

避免干扰的一种方法是在 Cython 中或作为 Python 扩展模块实现“进行测量”代码,并确保它将释放 GIL(无论是在运行时的大部分时间还是非常定期)。

© www.soinside.com 2019 - 2024. All rights reserved.