0

我正在研究 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();
    }
}
4

2 回答 2

2

可能做你想做的最简单ManualResetEvent的方法是用 - 正如你提到的 - a替换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();
于 2018-07-19T09:44:52.537 回答
1

看起来您正在使用一些实现基于事件的异步模式的类。如果您正在做的话async,您真正想要的是使用实现基于任务的异步模式的 API 。

值得庆幸的是,Microsoft 提供了有关使 EAP 看起来像 TAP 的具体指导:

包装基于事件的异步模式 (EAP) 实现比包装 APM 模式更复杂,因为 EAP 模式比 APM 模式有更多的变化和更少的结构。为了演示,以下代码包装了该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。

于 2018-07-19T09:44:32.623 回答