主线程忙时连续更新UI

时间:2013-07-08 16:02:40

标签: c# .net multithreading winforms

我希望在我的MainForm正在执行一些与数据库相关的工作时显示带有Progressbar的新表单。我已经读过你不想将UI传递给新的线程,所以我想知道解决这个问题的最佳解决方案是什么?

我无法在另一个线程上执行与数据库相关的工作,因为它使用了对UI和MainForm的十几个调用。我也不能使用Application.DoEvents(),因为MainForm的线程在一个类中工作,据我所知,你不应该对类的父类进行任何调用。

任何建议都将不胜感激!

修改

澄清我实际想知道的事情:

我的程序看起来有点像这样:

... Content = DatabaseClass.LoadContent();
ApplyContentToUI(Content);

我想做的是以下内容:

//Show a form here with a progressbar
... Content = DatabaseClass.LoadContent();
ApplyContentToUI(Content);
//Close the form here

但是,因为执行ApplyContentToUI的时间是aprox。 1秒我决定做以下事情:

//Show a form here with a progressbar
... Content = DatabaseClass.LoadContent();
//Close the form here
ApplyContentToUI(Content);

如果您和我不同,需要在重新加载用户界面时显示进度条,我建议您查看下面的评论和答案。

2 个答案:

答案 0 :(得分:9)

  

我无法在另一个线程上完成与数据库相关的工作

假。

建议:

正如许多人在评论中提到的那样,您应该重构代码以将业务逻辑与UI逻辑分开。每个.NET版本都有已知的模式和实践,可以实现清晰的分离。我已经将一些工具和链接放在他们的用法中供您查看。

工具:

的BeginInvoke

BeginInvoke用于在控件源自的线程上异步运行代码。这将允许在原始线程是UI线程的情况下发生UI操作。 BackgroundWorker将此用于ReportProgress。这将允许UI在长时间运行操作时保持响应。

这不应该直接嵌入到业务逻辑中。但是,它可以用于将消息中继回UI线程。

请参阅:

BackgroundWorker的

  • BackgroundWorker将在一个单独的线程上运行,它具有事件驱动的进展(.NET 2.0 +)。

BackgroundWorker是一个事件驱动的控件,它将在另一个线程上执行代码。它具有内置功能,可以使用ReportProgress向听众通知进度。

在幕后,BackgroundWorker使用Control.BeginInvoke ReportProgress,可以有效地分离业务逻辑。

请参阅:

分派器

  • Dispatcher可以将操作转发到UI线程(.NET 3.0 +);

为WPF添加了Dispatcher,但它仍然可以在WinForms中使用(尽管不太理想)。 Dispatcher将以与BeginInvoke相同的方式运行,并且可以同步或异步运行。

请参阅:

任务

  • Task(T)类使得从UI线程交织逻辑变得简单。 (.NET 4.0 +,带有TPL包的.NET 3.5+)。

添加了任务并行库,以提供一组并行和并发的抽象。 Task是一个消息驱动的,允许封装非阻塞操作。这也是.NET 4.5 async / await模式的基础。

Task用于流操作,以便它们可以链接在一起并生成结果。这比BackgroundWorker更精简,它可以更好地组合操作。这也使实施producer-consumer scenarios更简单。

请参阅:

答案 1 :(得分:1)

@Servy建议我不是混合UI和逻辑,而是首先执行逻辑,然后将结果应用到UI。虽然这不符合最初的目的,但它似乎是解决我问题的最简单方法。 (具有进度条的表单仅在执行逻辑时可见,但现在在更新UI时可见。)UI更新将在aprox中完成。因为时间很短,所以最后并不需要进度条。

对于那些在加载UI时仍然需要进度条的人,在这个问题下给出了一些好的答案/解决方案。