2

我找到了一个看起来非常简单的解决方案来解决我目前的情况。

我目前的情况是我想在一个新线程上执行一些 I/O 繁重的操作,这样我就不会陷入我的 GUI 线程。作为我的表单的成员,我编写了一个函数,它已经完成了这些 I/O 操作,但是在 GUI 线程上运行它确实让应用程序使用起来很痛苦。所以我的计划是在一个新的线程中运行这个函数。因此,我在表单中创建了一个 Thread 变量,并试图让它将该函数用作 ThreadStart 参数。不过,它似乎并不喜欢它。

我在这里找到了一个外观优雅的解决方案,作为对另一个线程的回应。

///...blah blah updating files
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
    someLabel.Text = newText; // runs on UI thread
});
///...blah blah more updating files

从该响应的外观来看,我可以在新线程中运行此函数,然后在线程完成计算后使用匿名函数更新我的表单。不过,我只是不够好,无法填补该回复中的空白。

我似乎读到的关于 Threads 的所有内容都表明我的 ThreadStart 函数需要是新类中的静态方法。该响应似乎表明我可以在我的 Form 类中执行此操作,以便this引用仍然引用我的 Form 实例。否则,如果我的 ThreadStart 参数是一个不同的类,我必须传入对 Form 实例的引用,这似乎需要更多代码,对吧?

有人介意帮我填写该回复的上下文吗?提前致谢!

4

1 回答 1

1

有很多方法可以做到这一点。许多版本都存在的一个非常简单直接的方法是使用BackgroundWorker。它正是为这种情况而设计的。它有一个DoWork在后台线程中运行的方法,以及一个Completed在工作完成后触发的事件,该事件在 UI 线程中运行(因此您不需要调用调用或任何东西来使用结果更新 UI)。它甚至支持报告进度(报告进度事件也在 UI 线程中运行),因此您可以轻松更新进度条或状态文本。

MSDN 也有一些示例,您可以通过一些简单的搜索找到更多示例。

通过 C# 4.0 提供的另一个选项是使用Tasks. 您可以启动一个将在后台线程中运行的新任务,然后您可以添加一个将在 UI 线程中的延续。

这是一个简单的例子:

private void button1_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew(() => doStuffInBackground())
        .ContinueWith(task => updateUI(), TaskScheduler.FromCurrentSynchronizationContext());
}

private void updateUI()
{
    throw new NotImplementedException();
}

private void doStuffInBackground()
{
    throw new NotImplementedException();
}

您当然可以在我拥有的实际 lambda 中做任何您想做的事情,或者您甚至可以删除 lambda 并将方法直接放入其中,只要您确保签名正确即可。如果需要,您还可以继续链接这些延续,例如,允许您执行任务 1,更新标签,然后执行任务 2,更新标签等。主要缺点是它不擅长更新进度条经常在一个循环里面,一路一个BackgroundWorker就可以了。

于 2012-07-24T16:34:07.913 回答