简体   繁体   English

使用TaskScheduler.FromCurrentSynchronizationContext更新Task中的UI

[英]update UI in Task using TaskScheduler.FromCurrentSynchronizationContext

I want to add some text to list box using Task and I simply use a button and place in click event this code: 我想使用“ 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);

but it does not work and UI locked until the end of the for loop. 但直到for循环结束后,它才起作用且UI锁定。

Where is the problem ? 问题出在哪儿 ?

thanks :) 谢谢 :)

You could do all work in the thread, but then when you need to update the UI use the dispatcher: 您可以在线程中完成所有工作,但是当您需要更新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);
  }
});

where is problem? 问题在哪里?

Well you're explicitly saying that you want to execute the task in the UI thread ... and then you're sleeping within the task, so it's blocking the UI thread. 好吧,您明确地说是要在UI线程中执行任务……然后您在任务中处于睡眠状态,因此它阻塞了UI线程。 How did you expect to be in the UI thread, but for Thread.Sleep not to cause a problem? 您期望如何在UI线程中工作,但是Thread.Sleep 不会引起问题?

If you can use C# 5 and async/await, that would make things much easier: 如果可以使用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);
    }
}

If you can't use C# 5 (as suggested by your tags), it's significantly trickier. 如果您不能使用C#5(如您的标签所建议),那将非常棘手。 You might be best off using a Timer : 您最好使用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();

As you can see, it's pretty ugly... and it assumes you don't want to actually do any work between UI updates. 如您所见,这非常丑陋……并且假设您实际上不想在UI更新之间做任何工作

Another alternative would be to simulate your long-running task (sleeping at the moment) on a different thread using BackgroundWorker , and use ReportProgress to come back to the UI thread to add the list item. 另一个选择是使用BackgroundWorker在另一个线程上模拟您的长时间运行的任务(此刻正在休眠),然后使用ReportProgress返回UI线程以添加列表项。

Based on the suggestions in this thread, I've come up with the following solution: 根据此线程中的建议,我提出了以下解决方案:

    public TaskGUIUpdater(Form1 form1, TaskDataGenerator taskDataGenerator)
    {
        Task.Factory.StartNew(() => {
            while (true)
            {
                form1.BeginInvoke(new Action(() => {
                    form1.UpdateGUI(taskDataGenerator.Counter, taskDataGenerator.RandomData);
                }));

                Thread.Sleep(1000);
            }
        });
    }

TaskGUIUpdater is a constructor in a class outside of the main form. TaskGUIUpdater是主窗体之外的类中的构造函数。 It takes a reference to the form that needs to be updated, as well as a reference of the task that it needs to get data from. 它引用了需要更新的表单,以及需要从中获取数据的任务的引用。

The form1.UpdateGUI method takes whichever data you want to set to form1, and then simply sets it to form1. form1.UpdateGUI方法采用要设置为form1的任何数据,然后只需将其设置为form1。

Just giving another flavour of the solution for c# 4.0. 只是为c#4.0提供了另一种解决方案。 This is similar to @Debora and @Jay (well, if you forget about the while(true)... just talking about the BeginInvoke) solutions, but fully based on TPL and the one that gets closer to the code generated with async/await: 这类似于@Debora和@Jay(好吧,如果您忘记了while(true)...只是在谈论BeginInvoke)解决方案,但完全基于TPL,并且它更接近于async /生成的代码等待:

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
    for (int i = 0; i < 10; i++)
    {
        Task.Factory.StartNew(() =>
        {
             listBox1.Items.Add("Number cities in problem = " + i.ToString());
        }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

        System.Threading.Thread.Sleep(1000);
    }
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);

Your work task should be scheduled using the default TaskScheduler (that will use the ThreadPool) and use the uiScheduler to callback to the UI thread when required to update the UI. 您的工作任务应该使用默认的TaskScheduler(将使用ThreadPool)进行计划,并在需要更新UI时使用uiScheduler回调到UI线程。 Keep in mind that this is a sample implementation and there could be some problems with this sample, for instance, the inner task is scheduled to execute on the UI thread, but it is not waited by the calling task, so the sleep will actually run while the inner task is running. 请记住,这是一个示例实现,此示例可能存在一些问题,例如,内部任务计划在UI线程上执行,但没有被调用任务等待,因此睡眠实际上会运行在内部任务运行时。 It is also very important that you do not wait for the tasks, or you could have a deadlock (inner task is trying to run on the UI thread that is blocked waiting for the outer task). 不要等待任务也很重要,否则可能会出现死锁(内部任务试图在被阻止等待外部任务的UI线程上运行)。

I usually use the uiScheduler on continuation task to provide the data to the UI. 我通常在延续任务上使用uiScheduler将数据提供给UI。 In your case it could be something like this: 在您的情况下,可能是这样的:

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
    //load data or perform work on background thread
    var itemList = new List<int>();
    for (int i = 0; i < 10; i++)
    {
        itemList.Add(i);
        System.Threading.Thread.Sleep(1000);
    }
    return itemList;
}).ContinueWith((itemList) => 
{
   //get the data from the previous task a continue the execution on the UI thread
   foreach(var item in itemList)
   {
      listBox1.Items.Add("Number cities in problem = " + item.ToString());
   }
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

The resulting compiled code will be very similar (if not equal) to the code generated with async/await 生成的编译代码将与async / await生成的代码非常相似(如果不相等)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 TaskScheduler.FromCurrentSynchronizationContext块UI - TaskScheduler.FromCurrentSynchronizationContext block ui .NET中的TaskScheduler.FromCurrentSynchronizationContext() - TaskScheduler.FromCurrentSynchronizationContext() in .NET Task.ContinueWith(...,TaskScheduler.FromCurrentSynchronizationContext())在UI线程上运行的任何场景? - Any scenario where Task.ContinueWith(…, TaskScheduler.FromCurrentSynchronizationContext()) would *not* run on the UI thread? 我如何使用TaskScheduler.FromCurrentSynchronizationContext()解决后台线程异常上的UI更新 - how do i use TaskScheduler.FromCurrentSynchronizationContext() to solve UI update on background thread exception 关于TaskScheduler.FromCurrentSynchronizationContext和Task.Factory.StartNew()的使用 - Regarding usage of TaskScheduler.FromCurrentSynchronizationContext & Task.Factory.StartNew() TaskScheduler.Current和TaskScheduler.FromCurrentSynchronizationContext()的区别? - TaskScheduler.Current and TaskScheduler.FromCurrentSynchronizationContext() difference? TaskScheduler.FromCurrentSynchronizationContext()是否表示该类的上下文? - Does TaskScheduler.FromCurrentSynchronizationContext() mean the context of the class? 为什么TaskScheduler.FromCurrentSynchronizationContext在Monotouch中不同步? - Why doesn't TaskScheduler.FromCurrentSynchronizationContext synchronize in Monotouch? 从工作线程调用TaskScheduler.FromCurrentSynchronizationContext异常 - TaskScheduler.FromCurrentSynchronizationContext exception when called from worker thread Async / Await等效于带有CancellationToken和TaskScheduler.FromCurrentSynchronizationContext()调度程序的.ContinueWith - Async/Await equivalent to .ContinueWith with CancellationToken and TaskScheduler.FromCurrentSynchronizationContext() scheduler
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM