【问题标题】:How can I wait for an async WCF service to complete?如何等待异步 WCF 服务完成?
【发布时间】:2012-12-19 11:35:32
【问题描述】:

这个问题总结得差不多了。我有一个 WCF 服务,我想等到它完成后再做其他事情,但必须等到它完成。我的代码看起来像这样。谢谢!

    private void RequestGeoCoordinateFromAddress(string address)
    {
        GeocodeRequest geocodeRequest = new GeocodeRequest();

        GeocodeServiceClient geocodeService = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");

        geocodeService.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(geocodeService_GeocodeCompleted);

        // Make the geocode request
        geocodeService.GeocodeAsync(geocodeRequest);

        //if (geocodeResponse.Results.Length > 0)
        //    results = String.Format("Latitude: {0}\nLongitude: {1}",
        //      geocodeResponse.Results[0].Locations[0].Latitude,
        //      geocodeResponse.Results[0].Locations[0].Longitude);
        //else
        //    results = "No Results Found";

        // wait for the request to finish here, so I can do something else
        // DoSomethingElse();
    }

    private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
    {
        bool isErrorNull = e.Error == null;
        Exception error = e.Error;

        try
        {
            double altitude = e.Result.Results[0].Locations[0].Latitude;
            double longitude = e.Result.Results[0].Locations[0].Longitude;

            SetMapLocation(new GeoCoordinate(altitude, longitude));
        }
        catch (Exception ex)
        {
            // TODO: Remove reason later
            MessageBox.Show("Unable to find address. Reason: " + ex.Message);
        }
    }

【问题讨论】:

  • 为什么不能将此代码放在GeocodeCompleted 事件处理程序中?
  • 嗯,没想到这一点。我会看看它是如何工作的。谢谢。
  • 您在使用 SilverLight 服务吗? SilverLight 仅支持异步。

标签: wcf asynchronous


【解决方案1】:

WCF 支持一种模式,调用具有异步 begin 调用和相应的 end 调用。

在这种情况下,异步方法将在客户端的接口中:

[ServiceContract]
interface GeocodeService
{
     // Synchronous Operations
     [OperationContract(AsyncPattern = false, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
     GeocodeResults Geocode(GeocodeRequestType geocodeRequest);

     // Asynchronous operations
     [OperationContract(AsyncPattern = true, Action="tempuri://Geocode", ReplyAction="GeocodeReply")]
     IAsyncResult BeginGeocode(GeocodeRequestType geocodeRequest, object asyncState);
     GeocodeResults EndGeocode(IAsyncResult result);
}

如果您使用带有异步调用选项的 svcutil 生成客户端界面,您将自动获得所有这些。如果您不使用自动生成客户端代理,您也可以手动创建客户端界面。

End 调用将阻塞,直到调用完成。

IAsyncResult asyncResult = geocodeService.BeginGeocode(geocodeRequest, null);
//
// Do something else with your CPU cycles here, if you want to
//

var geocodeResponse = geocodeService.EndGeocode(asyncResult); 

我不知道你对接口声明做了什么来获得 GeocodeAsync 函数,但如果你能将它重新调整为这种模式,你的工作会更容易。

【讨论】:

  • 我同意,这比使用 ManualResetEvent 简单得多。您无法保证 Web 服务已使用手动重置返回结果,但您正在使用 End 调用。
  • 哦,我真的很喜欢这个。我会试一试。谢谢!
  • 嗯,我想知道 GeocodeAsync 是如何产生的。它似乎没有遵循通常的异步模式。我会在我的答案中添加更多内容。
【解决方案2】:

你可以使用ManualResetEvent:

private ManualResetEvent _wait = new ManualResetEvent(false);

private void RequestGeoCoordinateFromAddress(string address)
{
    ...
    _wait = new ManualResetEvent(false);
    geocodeService.GeocodeAsync(geocodeRequest); 
    // wait for maximum 2 minutes
    _wait.WaitOne(TimeSpan.FromMinutes(2));
    // at that point the web service returned
}

private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
   ...
   _wait.Set();
}

显然这样做完全没有意义,所以这里的问题是:为什么需要这样做?如果要阻塞主线程,为什么要使用异步调用?为什么不使用直接调用呢?

通常在使用异步 Web 服务调用时,您不应阻塞主线程,而是在异步回调中完成处理结果的所有工作。根据应用程序的类型(WinForms、WPF),您不应该忘记 GUI 控件只能在主线程上更新,因此如果您打算在回调中修改 GUI,您应该使用适当的技术(InvokeRequired,... )。

【讨论】:

  • 问题是 WCF 服务似乎只有异步方法(GeocodeAsync()),所以我似乎被卡在异步执行该操作,这就是我希望它在我可以继续之前完成的原因.我正在 Completed 事件中尝试它,看看它是否可以从那里工作。
  • 顺便说一句,ManualResetEvent 不起作用,它首先等待 2 分钟,然后调用 Completed 事件。
  • 这不起作用,因为 geocodeService_GeocodeCompleted 在被 WaitOne 调用阻塞的主线程上运行。
【解决方案3】:

不要将此代码用于 Silverlight:

private ManualResetEvent _wait = new ManualResetEvent(false);

private void RequestGeoCoordinateFromAddress(string address)
{
    ...
    _wait = new ManualResetEvent(false);
    geocodeService.GeocodeAsync(geocodeRequest); 
    // wait for maximum 2 minutes
    _wait.WaitOne(TimeSpan.FromMinutes(2));
    // at that point the web service returned
}

private void geocodeService_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
{
   ...
   _wait.Set();
}

当我们调用 _wait.WaitOne(TimeSpan.FromMinutes(2)) 时,我们阻塞了 UI 线程,这意味着服务调用永远不会发生。在后台,对 geocodeService.GeocodeAsync 的调用实际上是放在了一个消息队列中,只有在线程不执行用户代码时才会被执行。如果我们阻塞线程,服务调用永远不会发生。

Synchronous Web Service Calls with Silverlight: Dispelling the async-only myth

【讨论】:

    【解决方案4】:

    Visual Studio 11 Beta 包含带有 async-await 的 C# 5。

    Async CTP - How can I use async/await to call a wcf service?

    这使得以“同步风格”编写异步客户端成为可能。

    【讨论】:

      【解决方案5】:

      我看到一个人确实使用了 ManualReset 和 waitAll,但他必须将所有代码包装在 ThreadPool 中。 这是一个非常糟糕的主意......认为它有效

      【讨论】:

        猜你喜欢
        • 2015-02-13
        • 2014-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-04
        • 2019-05-31
        • 1970-01-01
        相关资源
        最近更新 更多