任务 await 没有 return 结果

Task await does not return the result

我正在尝试编写一个使用 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 class

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();

我知道有一种死锁,但我不知道如何解决这个问题以及哪个对象。

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

这是一个有效的 .NET Fiddle

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

根据您目前分享的内容(并假设 client.loginAsync return 是 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 没有 return 您正在寻找的东西,那么您将需要采取这种做法 something similar to your current approach. Or if you are locked in to the event-based async pattern,您还有其他考虑因素 - 例如是否或你不想支持取消、IsBusy、进度、增量结果,如果你有能力让事件参数继承 System.ComponentModel.AsyncCompletedEventArgs 等...

最后一个考虑,如果 client.loginAsyncTask returning,即使它不是 return 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 似乎符合他的需求。