我正在寻找在图形应用程序中使用多线程的必要性的一个很好的解释。在下面的示例中使用了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)
控制台示例当然是单线程的,并且工作正常。现在,如果我们想要添加图形化进度条,单线程实现可能如下所示:
单线程gui
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时每两秒响应一次事件。最后一个示例是一个多线程实现,并在整个执行过程中保持响应。
多线程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,你必须使用多线程解决方案,这似乎是非常明确和合乎逻辑的。没有别的办法了。是这样的吗?我试图向其他开发人员多次解释,但许多人不理解或不同意。有人可以提供这个概念的解释,链接到它上面的文章,或者如果我的理解不正确则纠正我。
答案 0 :(得分:6)
您的理解是正确的。如果应用程序不是多线程的,则应用程序会等待每个操作完成。当您的应用程序是多线程时,您可以使用一个线程来处理GUI操作,使用另一个线程来处理文件。
我没有文章或类似的东西的引用。如果你认为线程是人,每个人都有自己的工作,每个人一次只做一件事,也许会有所帮助。
答案 1 :(得分:2)
主要原因是,GUI工具包处理mainloop
中的所有事件(鼠标移动,按钮单击,键盘输入,系统事件等),这不是您为GUI GUI应用程序编写的代码的一部分
此主循环调用您提供的所有事件处理程序和其他函数。现在,如果其中一个函数花费的时间过长(例如> 100ms),则会对UI响应性产生非常明显的影响,因为主循环将无法处理更多事件。
本主题应在任何有关GUI编程的书籍的“高级概念”部分中详细讨论,无论使用何种工具包的编程语言。
答案 2 :(得分:0)
多线程是实现这一目标的一种方式,但不一定是唯一(或最佳)方式。作为一个python示例,greenlets提供了在同一个线程中运行并发进程的方法,避免了与多线程相关的锁定问题。在大多数情况下,我肯定会认为greenlets是一种首选解决方案,因为它们相对容易编码。