在C#中异步等待事件

时间:2018-07-19 09:38:12

标签: c# wcf asynchronous taskcompletionsource

我正在使用WCF服务/客户端,试图弄清楚如何用不会阻塞调用者线程的东西替换ManualResetEvent。
最重要的是,await client.CloseAsync()事件触发之前,不会调用FinishedEventReceived

我曾经考虑使用TaskCompletionSource,但是我不确定在这种情况下该如何工作。

我知道代码有点丑陋,完全违背了使用异步编程的目的,我的策略。
有什么想法吗?

private async Task CallServiceMethodAndWaitForEvent()
{
    var mre = new ManualResetEvent(true);

    var client = new AwesomeClient();
    client.FinishedEventReceived += (s, e) =>
    {
        // Do something with the result, this event is only fired once.
        mre.Set();
    };
    client.UpdateEventReceived += (s, e) =>
    {
        // This even can fire several times before the finished event.
    };

    try
    {
        var parameters = new Parameters()
        {
            SomeParameter = "Test123",
            TestAmount = 10000,
        };

        var errors = await client.DoWorkAsync(parameters);
        Debug.WriteLine(errors);

        mre.WaitOne(TimeSpan.FromSeconds(20));
        await client.CloseAsync();
    }
    catch (FaultException ex)
    {
    }
    catch (Exception)
    {
        client.Abort();
    }
}

2 个答案:

答案 0 :(得分:2)

可能最简单的方式来完成您想要的操作,就是将ManualResetEvent替换为-TaskCompletionSource。例如:

var tcs = new TaskCompletionSource<int>();

var client = new AwesomeClient();
client.FinishedEventReceived += (s, e) =>
{
    // Do something with the result, this event is only fired once.
    tcs.SetResult(42); // number here is a dummy, since you only want Task
};

...

await tcs.Task;
await client.CloseAsync();

请注意,超时方面比较困难;一种常见的方法是使用Task.Delay作为后备,而Task.WhenAny,即

var timeout = Task.Delay(timeoutInterval);
if (timeout == await Task.WhenAny(timeout, tcs.Task))
    throw new TimeoutException();

答案 1 :(得分:1)

似乎您正在使用某个实现Event-based Asynchronous Pattern的类。如果您正在执行async,则真正想要的是使用实现Task-based Asynchronous Pattern的API。

非常感谢Microsoft提供有关adapting EAP to look like TAP的特定指南:

  

包装基于事件的异步模式(EAP)的实现要比包装APM模式更为复杂,因为与APM模式相比,EAP模式具有更多的变化和更少的结构。为了演示,以下代码包装了DownloadStringAsync方法。 DownloadStringAsync接受URI,在下载时引发DownloadProgressChanged事件以报告进度的多个统计数据,并在完成后引发DownloadStringCompleted事件。最终结果是一个字符串,其中包含指定URI处的页面内容。

public static Task<string> DownloadStringAsync(Uri url)
 {
     var tcs = new TaskCompletionSource<string>();
     var wc = new WebClient();
     wc.DownloadStringCompleted += (s,e) =>
         {
             if (e.Error != null) 
                tcs.TrySetException(e.Error);
             else if (e.Cancelled) 
                tcs.TrySetCanceled();
             else 
                tcs.TrySetResult(e.Result);
         };
     wc.DownloadStringAsync(url);
     return tcs.Task;
}

希望您可以适应正在使用的特定API。