任务 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.loginAsync
是 Task
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 似乎符合他的需求。
我正在尝试编写一个使用 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.loginAsync
是 Task
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 似乎符合他的需求。