同步方法中的异步调用

6
这是一个简单的例子:
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),它会破坏我的ActivityIndicator,冻结主线程,而且我觉得这是最糟糕的事情。还有一些Monitor.Wait的东西以及一些其他复杂的东西,但所有这些都似乎过于复杂,在简单场景中使用起来不太容易。
可能在不同的环境中也会有所不同,比如一些方法适用于WPF应用程序,一些则适用于其他应用程序等等,但应该有一个通用模式,不是吗?
你们怎么做到的呢?

8
让我们看看是否有人能够提供一个答案... - Paul Bellora
2
请考虑查看异步 CTP;这是一个预览,展示了在未来C#和VB中这种类型的代码将如何简化。如果它不能满足您的需求,我们希望在异步论坛上了解到这一点。有关详细信息,请参见http://msdn.com/async。 - Eric Lippert
1
哇!是Eric Lippert!我喜欢您的博客,先生! - iLemming
有人知道在MonoTouch中是否可以使用async CTP吗? - iLemming
是的,我喜欢它,它让我想起了自己有多愚蠢和懒惰。我不知道我还能在软件开发领域坚持多久。每次我打开你的博客并阅读一篇文章,我都会意识到自己的知识有多么匮乏,可悲的是我不知道要学多久才能达到不再感到如此愚蠢的程度......也许我天生就是这样——懒惰和愚蠢,但你的博客给了我希望。 - iLemming
1
@EricLippert:顺便问一下,因为您似乎与C# 5.0中的async有很多关系。async不是一个C#特性,而是一个.net库特性,对于C#来说,这不仅仅是创建一个新的关键字,并确保在编译时调用正确的代码片段吗?显然我不知道所有这些东西如何组合在一起,但是我很好奇像async这样的东西与其他选择(如TPL、PLINQ等)有何不同,您在日常工作中没有参与,对吗? - Joan Venge
1个回答

7
在.NET 4中,没有直接的方法来实现这一点。这与即将发布的C#中新的异步/等待功能非常吻合。
在.NET 4中,可以使用Task Parallel Library来实现此操作。您可以通过更改代码以返回Task<bool>来完成此操作,使调用者可以等待它(如果需要),或在任务上订阅一个继续运行的任务,在完成时运行。
为此,您需要像以下代码一样重新编写上面的代码:
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());
}

天哪,这似乎比拦截基本事件还要复杂,不是吗? - iLemming
1
@Agzam:这样做的巨大优势在于,你可以从两个地方进行操作,并且每个地方都可以以不同的方式处理结果。但是,对于单个事件来说,很难知道“哪一个”请求已经完成了... - Reed Copsey
2
另一个Reed解决方案的优点是它是“await-ready”的 - 在VS2011中,您将能够更改SomeMethod以使用bool result = await CoockinRequestAsync();,而CoockinRequestAsync不需要做任何更改。 - Stephen Cleary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接