多线程:如何更新UI以指示进度

时间:2009-07-07 18:57:37

标签: c# .net multithreading

自2008年圣诞节以来,我一直在研究同一个项目。我被要求从一个控制台应用程序(它只打印出跟踪语句)到完整的Windows应用程序。当然,那没关系。唯一的问题是应用程序的某些部分可能需要几分钟到几乎一个小时才能运行。我需要多线程来显示用户状态或错误。但我不知道从哪里开始。

我已经在WPF中构建了一个小UI。这是非常基本的,但我想根据需要扩展它。该应用程序通过选择源,选择目标,然后单击开始来工作。我希望随着流程的进行更新列表框。与SQL Server安装的方式大致相同,每个步骤在完成时都会有一个绿色的复选标记。

新手如何开始多线程?我应该查看哪些图书馆?任何建议都将不胜感激。

P.S。我目前正在阅读这个图书馆,http://www.codeplex.com/smartthreadpool

@Martin:我的应用程序是如何构建的:

  1. 引擎:按预定义顺序运行所有主要组件
  2. Excel:我写的包裹COM以打开/读取/关闭/保存工作簿
  3. 的库
  4. 图书馆:了解不同类型工作簿格式的图书馆(共5页)
  5. 商务课程:我编写的课程用于翻译Excel数据并为Access准备
  6. Db Library:我编写的一个使用ADO.NET读取Access数据的库
  7. AppSettings:你明白了
  8. Serialier:在应用程序崩溃的情况下保存数据
  9. 我使用从LINQ到ADO.NET的所有内容来获取数据,转换它,然后输出它。

    我的主要要求是我想更新我的用户界面以表明进度

    @Frank:如果后台工作程序中的某些内容抛出异常(已处理或以其他方式),会发生什么?我的申请如何收到通知?

    @Eric Lippert:是的,我正在调查这个问题。在我复杂化之前。

    如果您需要更多信息,请与我们联系。目前我已经从单元测试运行这个应用程序,所以我想callig它是一个控制台应用程序是不正确的。我使用Resharper来做到这一点。我是现在唯一使用该应用程序的人,但我想要一个更具吸引力的界面

9 个答案:

答案 0 :(得分:6)

我认为您没有指定正在使用的CLR的版本,但您可以查看“BackgroundWorker”控件。这是实现多个线程的简单方法。

最好的部分是,a part of the CLR 2.0 and up

更新以响应您的更新:如果您希望能够更新用户界面中的进度 - 例如在进度条中 - 后台工作人员是完美的。它使用我认为被称为的事件:ProgressChanged来报告状态。这是非常优雅。另外,请记住,您可以拥有所需的实例数,并且可以同时执行所有实例(如果需要)。

回答您的问题:您可以轻松设置示例项目并测试您的问题。我确实找到了以下内容,here(在评论中,谨慎的第2段):

  

如果操作引发异常   您的代码无法处理,   BackgroundWorker捕获异常   并把它传递给   RunWorkerCompleted事件处理程序,   它作为错误公开的地方   的财产   System.ComponentModel .. ::。RunWorkerCompletedEventArgs。

答案 1 :(得分:4)

来自Joseph Albahari的

Threading in C#非常好。

答案 2 :(得分:0)

总新手线程化的最佳方式可能就是线程池。我们可能需要更多地了解这些部分以获得更深入的建议

EDIT ::
由于我们现在有更多的信息,我将坚持我以前的答案,看起来你有大量的任务需要做,最好的方法来做任务加载是将它们添加到线程池和然后继续检查它们是否已完成,如果需要按特定顺序完成任务,那么您可以简单地添加下一个完成前一个。线程池对于这种事情确实相当不错,我认为在这种情况下我没有理由不使用它

答案 3 :(得分:0)

This page是一个很好的线程摘要。

听起来你可能不需要任何非常复杂的东西 - 如果你只是启动任务然后想知道它什么时候完成,你只需要几行代码来创建一个新的线程并获得它运行你的任务。然后您的UI线程可以随意乱丢并定期检查任务是否已完成。

答案 4 :(得分:0)

Concurrent Programming on Windows是关于这个主题的最佳书籍。由多线程的着名微软大师Joe Duffy撰写。从Windows线程调度程序的工作方式到.NET Parallels Extensions Library,您需要知道的一切以及更多内容。

答案 5 :(得分:0)

请记住创建代理以更新UI,这样您就不会遇到跨线程问题,并且UI似乎没有冻结/锁定

此外,如果你需要很多音符/电源点等等

我可以建议我本科生的所有讲义 http://ist.psu.edu/courses/SP04/ist411/lectures.html

答案 6 :(得分:0)

杰森的链接是一篇很好的文章。您需要注意的是UI只能由主UI线程更新,如果您尝试在工作线程中执行此操作,您将获得交叉线程异常。 BackgroundWorker控件可以帮助您处理事件,但您还应该了解Control.Invoke(或Control.Begin / EndInvoke)。这可以用于在UI线程的上下文中执行委托。

此外,您应该阅读从不同线程访问相同代码/变量的问题,其中一些问题可能会导致间歇性和难以追踪的错误。

需要注意的一点是,volatile关键字只保证变量访问的“新鲜度”,例如,它保证变量的每次读写都来自主内存,而不是来自线程或处理器缓存或其他内存模型的“特征”。它不会阻止线程在读取 - 更新 - 写入过程中被另一个线程中断(例如更改变量值)。这会导致错误,其中2个线程具有不同(或相同)的变量值,并且可能导致值丢失,2个线程具有相同的值,当它们应该具有不同的值时等等。您应该使用锁定/监视器(或其他线程同步方法,等待句柄,互锁增量/减量等)以防止这些类型的问题,这保证只有一个线程可以访问该变量。 (Monitor还具有隐式执行易失性读/写的优势)

正如其他人已经注意到的那样,您还应该尝试避免在等待后台线程完成时阻止您的UI线程,否则您的UI将无法响应。您可以通过让工作线程引发UI订阅的指示进度或完成的事件来完成此操作。

马特

答案 7 :(得分:0)

Typemock有一个名为Racer的新工具,用于帮助解决多线程问题。它有点先进,但你可以在他们的论坛和其他在线论坛上得到帮助(一个奇怪的想法是stackoverflow :-))

答案 8 :(得分:0)

我也是多线程的新手,但我同意Frank的观点,后台工作者可能是你最好的选择。它通过事件订阅工作。以下是您如何使用它的基础知识。

  • 首先实例化新的后台工作者
  • 您的代码中为后台工作者主要事件订阅的方法:
    • DoWork:这应该包含需要很长时间才能处理的代码
    • ProgressChanged:只要从订阅DoWork的方法内部调用ReportProgress(),就会被调用
    • RunWorkerCompleted:在DoWork方法完成时调用

当您准备好运行耗时的过程时,请调用后台worker的RunAsync()方法。这将在单独的线程上启动DoWork方法,然后可以通过ProgressChanged事件报告它的进度。一旦完成,将引发RunWorkerComplete。

DoWork事件方法还可以通过检查CancelPending属性的值来检查用户是否以某种方式请求取消进程(调用CanceLAsync())。