任务等待不会返回结果

时间:2017-01-18 08:57:54

标签: c# async-await task deadlock

我正在尝试编写一个使用Task和TaskCompletion的函数。

我的问题是登录后,结果不会返回。之前我使用过类似的代码,它正在运行。我不知道造成这种情况的原因。

public async Task<byte[]> Sign(byte[] documentContent)
{
    var service = new SignServiceWrapper("https://example.com?wsdl");
    var loginResult = await Task.Run(() => service.Login(loginRequest));

    //....
}

和我的SignServiceWrapper类

public class SignServiceWrapper
{
    private static string _webServiceUrl;
    private  BrokerClientClient client;

    public SignServiceWrapper(string webServiceUrl)
    {
        _webServiceUrl = webServiceUrl;
    }

    public Task<loginResponse> Login(loginRequest request)
    {
        var tcs = new TaskCompletionSource<loginResponse>();

        ClientGenerator.WebServiceUrl = _webServiceUrl;

        ClientGenerator.InitializeService(); 
        client = ClientGenerator.ServiceClient;   

        client.loginCompleted += (sender, loginResult) =>
        {
            if (loginResult.Error != null)
                tcs.SetException(loginResult.Error);
            else
                tcs.TrySetResult(loginResult.Result);
        };    

        client.loginAsync(request);                

        return tcs.Task;
    }

    // ...
}

如果我这样调用我的登录功能

var loginResult = Task.Run(() => service.Login(loginRequest));
loginResult.Wait();

我知道有一种僵局,但我不知道如何在这里解决这个问题以及对象。

2 个答案:

答案 0 :(得分:2)

这是一个有效的.NET Fiddle

我认为你的.Login方法试图做得太多。我注意到的第一件事(并且只能想象它是如何实现的)是静态ClientGenerator,它具有静态可变状态。这是令人震惊的,非常具体的代码味道。我希望看到客户端本身的样子以及如何实现,因为这肯定有助于更好地回答这个问题。

根据您目前所分享的内容(并假设client.loginAsync返回Task<loginResponse>),我会说您可以执行以下操作:

public class SignServiceWrapper
{
    private static string _webServiceUrl;
    private  BrokerClientClient client;

    public SignServiceWrapper(string webServiceUrl)
    {
        _webServiceUrl = webServiceUrl;
    }

    public Task<loginResponse> LoginAsync(loginRequest request)
    {
        ClientGenerator.WebServiceUrl = _webServiceUrl;
        ClientGenerator.InitializeService();
        client = ClientGenerator.ServiceClient;

        return client.loginAsync(request);
    }

    // ...
}

然后你可以这样消费:

public async Task<byte[]> Sign(byte[] documentContent)
{
    var service = new SignServiceWrapper("https://example.com?wsdl");
    var loginResult = await service.LoginAsync(loginRequest);

    //...
}

如果client.loginAsync没有返回您要查找的内容,那么您需要执行此操作something similar to your current approach。或者,如果您被锁定在event-based async pattern,则还有其他注意事项 - 例如您是否要支持取消,IsBusy,进度,增量结果以及是否有能力使用事件参数继承System.ComponentModel.AsyncCompletedEventArgs等......

最后一个考虑因素,如果client.loginAsync返回Task,即使它没有返回loginResponse,您也需要等待它:

public async Task<loginResponse> Login(loginRequest request)
{
    var tcs = new TaskCompletionSource<loginResponse>();

    ClientGenerator.WebServiceUrl = _webServiceUrl;
    ClientGenerator.InitializeService();
    client = ClientGenerator.ServiceClient;
    client.loginCompleted += (sender, loginResult) =>
    {
        if (loginResult.Error != null)
            tcs.SetException(loginResult.Error);
        else
            tcs.TrySetResult(loginResult.Result);
    };    

    await client.loginAsync(request);                

    return tcs.Task;
}

<强>更新

与OP讨论后,这.NET Fiddle似乎符合他的需要。

答案 1 :(得分:-1)

更改var loginResult = await Task.Run(() =>service.Login(loginRequest));var loginResult = await service.Login(loginRequest);

相关问题