I am working with TPL and need to have a long running TPL task send results to the parent UI thread without terminating. I have tried several approaches and have been googling quite a bit. Does anyone know how to make this happen with TPL?
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
You could pass in a delegate to call with periodic results, and a SynchronizationContext
which the task could use to invoke the callback on the correct thread. That's basically the way that BackgroundWorker
does it (and the way that the async feature of C# 5 will "know" where to call you back) - it captures SynchronizationContext.Current
on the calling thread, then calls Post
(IIRC) to post a message to the right context. You then just need to wrap the original callback in a SendOrPostCallback
which executes it when it's got to the right thread.
EDIT: Sample program:
using System;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;
class Test
{
static void Main()
{
Form form = new Form();
Label label = new Label();
form.Controls.Add(label);
form.Load += delegate { HandleLoaded(label); };
Application.Run(form);
}
static void HandleLoaded(Label label)
{
Action<string> callback = text => label.Text = text;
StartTask(callback);
}
static void StartTask(Action<string> callback)
{
SendOrPostCallback postCallback = obj => callback((string) obj);
SynchronizationContext context = SynchronizationContext.Current;
Task.Factory.StartNew(() => {
for (int i = 0; i < 100; i++)
{
string text = i.ToString();
context.Post(postCallback, text);
Thread.Sleep(100);
}
});
}
}
回答2:
Depending on the application you are using there might be different approaches.
- WinForms: Control.Invoke
- WPF/Silverlight/WP7 and derivates: Dispatcher.Invoke
- Some other type of application: define what UI thread means first?