【问题标题】:Asynchronous call in synchronous method同步方法中的异步调用
【发布时间】:2011-10-20 21:00:10
【问题描述】:

这是一个简单的例子:

public event EventHandler CookinDone = delegate{};

public void CoockinRequest(){
    var indicator = new ActivityIndicator();
    ActivityIndicator.Show("Oooo coockin' something cool");

    var bw = new BackgroundWorker();    
    bw.DoWork += (sender, e) => CockinService.Cook();
    bw.RunWorkerCompleted += (sender, e) => {
       indicator.Hide();
       CookinDone.Invoke(this,null);
    };

    bw.RunWorkerAsync();
}

现在,每次我使用该方法时,我都必须拦截 CookinDone 事件并继续前进。

var cook = new Cook();
cook.CookinDone += (sender, e) => MessageBox.Show("Yay, smells good");
cook.CoockinRequest();

但是如何通过将方法的返回类型设置为布尔值并在 Cookin 完成时返回结果来简化这一点?

if (CoockinRequest()) MessageBox.Show('Yay, smells even better');

如果我把while (bw.IsBusy) 之类的东西放在那里,它会搞砸我的ActivityIndi​​cator,冻结主线程,我觉得这将是最糟糕的事情。还有一些Monitor.Wait 的东西和一些其他的东西,比如TaskFactory,但是所有这些东西似乎都太复杂了,无法在简单的场景中使用。

在不同的环境中也可能有所不同,比如某些方法适用于 WPF 应用程序,有些方法适用于其他应用程序等等,但应该有一个通用模式不是吗?

你们是怎么做到的?

【问题讨论】:

  • 让我们看看有没有人回答...
  • 考虑看看异步 CTP;这是对未来如何在 C# 和 VB 中简化此类代码的一瞥。如果它不能满足您的需求,那么我们想在异步论坛上了解这一点。详情请见msdn.com/async
  • 天啊!是埃里克·利珀特!我爱你的博客先生!
  • 有人知道异步 CTP 是否可以与 MonoTouch 一起使用吗?
  • @EricLippert:顺便说一句,我想知道,因为您似乎与 C# 5.0 中的异步内容有关。异步不是 .net 库功能,而不是 C# 功能吗?从 C# 的角度来看,这不仅仅是创建一个新的关键字并确保在编译时调用正确的代码片段吗?显然,我不知道这一切是如何结合在一起的,但我很想知道像 async 这样的东西与 TPL、PLINQ 等其他替代方案有何不同,在这些替代方案中,你没有像你的日常工作那样参与 AFAIK?

标签: c# multithreading c#-4.0


【解决方案1】:

在 .NET 4 中没有直接的方法来执行此操作。这实际上非常符合下一版 C# 中的新异步/等待功能。

现在可以在 .NET 4 中使用任务并行库来完成此任务。您可以通过更改代码以返回 Task<bool> 来实现此目的,因此调用者可以等待它(如果需要),或者订阅完成后将运行的任务的 continuation

为此,您需要像这样重写上面的代码:

public Task<bool> CoockinRequestAsync()
{
    var indicator = new ActivityIndicator();
    ActivityIndicator.Show("Oooo coockin' something cool");

    // This assumes Cook() returns bool...
    var task = Task.Factory.StartNew(CockinService.Cook);

    // Handle your removal of the indicator here....
    task.ContinueWith( (t) => 
       {
           indicator.Hide();
       }, TaskScheduler.FromCurrentSynchronizationContext());

    // Return the task so the caller can schedule their own completions
    return task;
}

然后,当你去使用它时,你会写这样的东西:

private void SomeMethod()
{
    var request = this.CoockinRequestAsync();

    request.ContinueWith( t =>
    {
        // This will run when the request completes... 
        bool result = t.Result;

        // Use result as needed here, ie: update your UI

    }, TaskScheduler.FromCurrentSynchronizationContext());
}

【讨论】:

  • 天哪,在我看来,这让事情变得比仅仅拦截基本事件更复杂,不是吗?
  • @Agzam:不过,这具有巨大的优势,可以让您从两个地方执行此操作,并且每个地方都可以以不同的方式处理结果。对于单个事件,要知道“哪些”请求正在完成是非常棘手的......
  • +1。 Reed 解决方案的另一个优点是它“准备就绪”——在 VS2011 中,您可以将SomeMethod 更改为使用bool result = await CoockinRequestAsync();,而CoockinRequestAsync 根本不需要更改。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-19
  • 2017-09-23
  • 2015-11-09
  • 1970-01-01
相关资源
最近更新 更多