【问题标题】:Explanation of need for Multi Threading GUI programming多线程GUI编程需求说明
【发布时间】:2013-10-04 13:51:55
【问题描述】:

我正在寻找关于在图形应用程序中使用多线程的必要性的一个很好的解释。在下面的示例中使用了 Python,但问题不是 Python 特定的,它可能适用于任何语言的图形编程的一般设计。

让我们举一个简单的例子。让我们假设有一个应用程序对一组文件执行某种耗时的操作,并将它的进度输出到控制台。让我们假设这个操作每个文件需要 2 秒,并且有 10 个文件需要处理,分别称为 1.txt、2.txt、3.txt、... 10.txt。那么示例实现可能如下所示:

控制台

import time
def process(file):
    print 'processing {0}...'.format(file)
    time.sleep(2.0) #simulate slow operation

files = ['{0}.txt'.format(i) for i in range(1, 11)]
map(process, files)

控制台示例当然是单线程的,并且可以很好地完成工作。现在,如果我们想添加图形进度条,单线程实现可能如下所示:

单线程界面

import time, gtk, gobject

def process(file):
    print 'processing {0}...'.format(file)
    time.sleep(2.0)

class MainWindow(gtk.Window):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.progress = gtk.ProgressBar()
        self.progress.set_fraction(0)
        self.add(self.progress)
        self.connect("destroy", gtk.main_quit)
        self.show_all()
        files = ['{0}.txt'.format(i) for i in range(1, 11)]
        gobject.timeout_add(100, self.submit, files, 0)

    def submit(self, files, i):
        process(files[i])
        self.progress.set_fraction((i + 1.0)/len(files))
        if i + 1 < len(files):
            gobject.idle_add(self.submit, files, i + 1)

win = MainWindow()
gtk.main()

这似乎工作正常,但是当您运行应用程序时,如果您尝试与应用程序交互,例如尝试调整窗口大小,它会卡住,并且只有在被释放以处理挂起的 gui 时每两秒响应一次事件。最后一个示例是多线程实现,并在整个执行过程中保持响应。

多线程界面

import time, gtk, gobject, threading

def process(file):
    print 'processing {0}...'.format(file)
    time.sleep(2.0)

class MainWindow(gtk.Window):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.progress = gtk.ProgressBar()
        self.progress.set_fraction(0)
        self.add(self.progress)
        self.connect("destroy", gtk.main_quit)
        self.show_all()
        files = ['{0}.txt'.format(i) for i in range(1, 11)]
        threading.Thread(target=self.submit, args=(files,)).start()

    def submit(self, files):
        for i, file in enumerate(files):
            process(file)
            gobject.idle_add(self.progress.set_fraction, (i + 1.0)/len(files))
            if not self.get_visible():
                return

gtk.gdk.threads_init()
win = MainWindow()
gtk.main()

在我看来,如果您的代码中有一个长时间运行的阻塞操作并且您想要一个响应式 gui,那么您必须使用多线程解决方案。没有其他方法可以解决它。是这样吗?我曾多次尝试向其他开发人员解释这一点,但许多人不理解或不同意。有人可以提供有关此概念的解释,相关文章的链接,或者如果我的理解不正确,请纠正我。

【问题讨论】:

  • 你的理解是正确的。假设您有一个压缩大文件的 GUI 应用程序。如果您单击一个按钮并开始压缩(单线程),您肯定会看到窗口冻结并且不会发生重绘。a

标签: python user-interface


【解决方案1】:

你的理解是正确的。如果应用程序不是多线程的,则应用程序会等待每个操作完成。当您的应用程序是多线程时,您使用一个线程来处理 GUI 操作,而另一个线程来处理文件。

我没有文章或类似内容的参考。如果您将线程视为人,可能会有所帮助,每个人都有自己的工作,每个人一次只能做一件事。

【讨论】:

  • 我建议将“进程”一词替换为“操作”或其他术语,因为进程可能会被解释为操作系统进程并且它是模棱两可的。
  • 我愿意,但我更喜欢把选择权留给作者 :)
【解决方案2】:

主要原因是,GUI 工具包处理 mainloop 中的所有事件(鼠标移动、按钮单击、键盘输入、系统事件等),这不是您为 GUI 应用程序编写的代码的一部分。

这个主循环调用您提供的所有事件处理程序和其他函数。现在,如果其中一个函数耗时过长(例如 > 100 毫秒),则会对 UI 响应产生非常明显的影响,因为主循环将无法处理更多事件。

无论使用何种工具包的编程语言,都应在任何有关 GUI 编程的书籍的“高级概念”部分中详细讨论该主题。

【讨论】:

    【解决方案3】:

    多线程是解决此问题的一种方法,但不一定是唯一(或最佳)方法。作为 python 示例,greenlets 提供了一种在同一线程中运行并发进程的方法,避免了与多线程相关的锁定问题。在大多数情况下,我当然会认为 greenlets 是首选解决方案,因为它们相对容易编码。

    【讨论】:

    • 您能否使用 Greenlet 发布一些代码,使用相同的处理函数并列出问题中的文件。我很想比较和对比。
    • 我不认为你说的是​​正确的。您能否使用任何具有阻塞调用并且仍然可以具有响应式 GUI 的库发布示例图形 python 代码。这没有任何意义。
    • 抱歉,我的回答并不完全清楚。我正在回答并发是否==多任务的一般问题。要使用 greenlet 重新编码您的示例,需要更多详细信息 process_files 函数,因为这是您想要封装在 greenlet 中的函数,如果无法访问,那么您肯定需要多线程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    相关资源
    最近更新 更多