【问题标题】:how to get a task completion notification in non gui thread如何在非 gui 线程中获取任务完成通知
【发布时间】:2011-07-25 18:00:44
【问题描述】:

背景:我曾经在表单加载期间调用存储过程。然而,由于这导致了次优的 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 线程上获得任务完成回调?每当任务完成并且如果出现问题并且仅在出现问题时(布尔结果返回 false),用户就会弹出一个消息框。在那之前,他可以继续在表单上做其他与数据库无关的事情。请告知我如何在非 gui 线程中获得任务完成回调和任务结果。谢谢

【问题讨论】:

  • 这在我看来应该已经可以工作了——你能发布 mycallback() 方法的代码或伪代码吗?
  • 我相信您可以使用 TaskScheduler.Default 来获取未绑定到同步上下文的调度程序。您看到这个问题的事实表明 TaskScheduler.Current 在某些时候被设置为同步任务调度程序。
  • @Stuart。如果其他人对此问题的建议不起作用,将发布一些示例代码。谢谢
  • @Dan 感谢您的提示。我没有使用任务调度程序设置任何内容。代码就像我所展示的那样。也会看那个角度。

标签: c# multithreading .net-4.0 task-parallel-library


【解决方案1】:

所有这些东西都在异步语言扩展中得到了最好的解决,您可以下载here 并拥有主页here

它在 C# 和 VB 中引入了 asyncawait 关键字,使您可以编写代码,即使在单一方法中也可以毫不费力地在 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

【讨论】:

  • 哇。整洁的东西。但是对于我的简单需求来说有点矫枉过正。我会阅读这个。谢谢
【解决方案2】:

我个人会为此使用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;
}

【讨论】:

  • 虽然这确实有效,但后台线程将被阻塞,等待 SP 返回。无论何时处理任何类型的 I/O,都应使用适当的异步 API。
【解决方案3】:

您应该考虑使用异步 API,而不是在后台线程中调用同步版本:

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.beginexecutenonquery.aspx

这样做的好处是不会阻塞任何线程,并且我相信回调将在 ThreadPool 线程上调用,例如不在 GUI 线程上。从那里,您可以使用 Invoke/BeginInvoke 将任何 GUI 调用编组回 GUI 线程。

【讨论】:

【解决方案4】:

为什么不这样做:

Task.Factory.StartNew(()=>WorkerMethod());

并将 WorkerMethod() 定义为:

void WorkerMethod()
{
     RunSPAndCheckStuff(); // this blocks until finished
     DoSomeMoreStuff(); // then this continuous
}

否则,请提供有关您要完成的任务的更多详细信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 1970-01-01
    • 2012-03-06
    • 2013-11-08
    • 1970-01-01
    相关资源
    最近更新 更多