如何在非GUI线程中获得任务完成通知
本文关键字:任务 通知 GUI 线程 | 更新日期: 2023-09-27 18:03:42
Background:我过去常常在Form Load期间调用一个存储过程。但是,由于这导致了次优的UI体验,所以我将SP调用放在Shown
事件中单独的任务中。由于这通常是表单显示过程中的最后一个事件,因此它比在表单加载事件中放置内容产生了更好的体验。我:
private void MainForm_Shown(object sender, EventArgs e)
{
dbCheckTask = Task<bool>.Factory.StartNew(RunSPAndCheckStuff());
// both of below run on the GUI thread.
// if I print the thread ID in mycallback it is the GUI thread id
dbCheckTask.ContinueWith(mycallback());
// I also tried below. But obviously, that too runs on the GUI thread
mycallback(dbCheckTask.Result)
}
因为它们是在GUI线程上触发的,所以我的启动表单绘制仍然既不是即时的也不是平滑的。我如何在非gui线程上获得任务完成回调而不诉诸事件?每当任务完成时,如果出现错误,并且只有当出现错误(bool结果返回false)时,用户才会收到弹出的消息框。在此之前,他可以继续在表单上做其他与数据库无关的事情。请告知我如何在非gui线程中获得任务完成回调与任务结果。谢谢你
所有这些东西都在Async语言扩展中得到了最好的解决,你可以在这里下载并在这里有主页。
它向c#和VB引入了async
和await
关键字,这将使您编写的代码可以在UI和后台线程之间轻松地来回切换,甚至在单个方法中。编译器会透明地将其转换为任务,延续,错误捕获等等,而你不必担心这些。你会感兴趣的例子是:
public async void AsyncSwitchToCPU() {
Console.WriteLine("On the UI thread.");
// Switch to a thread pool thread:
await new SynchronizationContext().SwitchTo();
Console.WriteLine("Starting CPU-intensive work on background thread...");
int result = DoCpuIntensiveWork();
Console.WriteLine("Done with CPU-intensive work!");
// Switch back to UI thread
await Application.Current.Dispatcher.SwitchTo();
Console.WriteLine("Back on the UI thread. Result is {0}.", result);
}
public int DoCpuIntensiveWork()
{
// Simulate some CPU-bound work on the background thread:
Thread.Sleep(5000);
return 123;
}
这甚至有一个上线许可(有一些来自MS的保留)。从f#中借来的非常优雅的东西。
Rgds Gert-Jan
我个人使用BackgroundWorker
。让回调在任务线程上运行的一种方法是修改方法调用和任务创建,如下所示:
private void MainForm_Shown(object sender, EventArgs e)
{
dbCheckTask = Task<bool>.Factory.StartNew(() => RunSPAndCheckStuff(mycallback));
...
}
private bool RunSPAndCheckStuff(Action<bool> callback)
{
bool result = false;
// Do stuff
callback(result);
return result;
}
你应该考虑使用异步API,而不是在后台线程中调用同步版本:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.beginexecutenonquery.aspx这样做的好处是没有线程会被阻塞,并且我相信回调将在ThreadPool线程上调用,例如不在GUI线程上调用。从那里,您可以使用Invoke/BeginInvoke将任何GUI调用封送回GUI线程。
为什么不这样做呢:
Task.Factory.StartNew(()=>WorkerMethod());
并定义WorkerMethod()为:
void WorkerMethod()
{
RunSPAndCheckStuff(); // this blocks until finished
DoSomeMoreStuff(); // then this continuous
}