使用TaskScheduler.FromCurrentSynchronizationContext更新Task中的UI

Ari*_*ian 1 c# multithreading task c#-4.0

我想在列表框中添加一些文本Task,我只需使用一个按钮并在点击事件中放置此代码:

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
    for (int i = 0; i < 10; i++)
    {
        listBox1.Items.Add("Number cities in problem = " + i.ToString());
        System.Threading.Thread.Sleep(1000);
    }
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);
Run Code Online (Sandbox Code Playgroud)

但是直到for循环结束它才能工作并且UI被锁定.

问题出在哪儿 ?

谢谢 :)

小智 7

您可以在线程中完成所有工作,但是当您需要更新UI时,请使用调度程序:

Task.Factory.StartNew(() =>
{
  for (int i = 0; i < 10; i++)
  {
     Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
        new Action(() => {listBox1.Items.Add("Number cities in problem = " + i.ToString()); }));
     System.Threading.Thread.Sleep(1000);
  }
});
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 5

哪里有问题?

好吧,你明确地说你想在 UI 线程中执行任务......然后你在任务中休眠,所以它阻塞了 UI 线程。您如何期望进入 UI 线程,但不会Thread.Sleep 引起问题?

如果您可以使用 C# 5 和 async/await,那会让事情变得更容易:

private static async Task ShowCitiesAsync()
{
    for (int i = 0; i < 10; i++)
    {
        listBox1.Items.Add("Number cities in problem = " + i);
        await Task.Delay(1000);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不能使用 C# 5(如您的标签所建议),那么它会更加棘手。您可能最好使用Timer

// Note: you probably want System.Windows.Forms.Timer, so that it
// will automatically fire on the UI thread.
Timer timer = new Timer { Interval = 1000; }
int i = 0;
timer.Tick += delegate
{
    listBox1.Items.Add("Number cities in problem = " + i);
    i++;
    if (i == 10)
    {
        timer.Stop();
        timer.Dispose();
    }
};
timer.Start();
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它非常丑陋......并且它假设您不想在 UI 更新之间实际执行任何工作

另一种选择是使用 ,在不同的线程上模拟长时间运行的任务(目前正在睡眠)BackgroundWorker,并使用ReportProgress返回 UI 线程来添加列表项。