Task.Wait 总是返回 false 虽然任务完成
Posted
技术标签:
【中文标题】Task.Wait 总是返回 false 虽然任务完成【英文标题】:Task.Wait always returns false although task finished 【发布时间】:2017-09-25 00:22:51 【问题描述】:我正在使用HttpClient
尝试在 Web API 控制器中执行 POST 方法。控制器方法是同步的。我是这样做的:
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
之后我打电话给Wait
:
var result = response.Wait(15000);
运行此代码时,我看到 http 完成执行,但 result
值始终为 false。我会错过什么?
编辑: 我现在尝试了一种异步方法,但它也没有帮助我
public IHttpActionResult Add(Item item)
var result = _db.AddItem(item);
return Ok(result);
测试项目:
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
return await
_owinTestServer.HttpClient.PostAsJsonAsync(url,body);
public async Task<ItemPreview> Method2(object body);
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
[TestMethod]
public void test1()
Item item = new(...);
Method2(item).Continue with(task => // Never reach here
我做错了什么? 调试时,我看到控制器方法返回了一个很好的响应,但它永远不会回到我的测试中
【问题讨论】:
如果您使用异步 API,调用Wait()
有什么意义?你最好使用await
。
因为没有办法使用HttpClient
以同步方式发布数据,否则我会同步进行。由于某种原因,当我尝试使用 await
时遇到了死锁
首先,我不会在async
中推荐Wait
,始终使用await
。其次,请发布导致死锁的完整代码,这样我们就可以对实际问题进行排序,而不是混合阻塞和非阻塞代码。
@Razort4x 我发布了我的代码
【参考方案1】:
您正在混合异步调用和阻塞调用(即.Result
、.Wait()
),这会导致死锁。
这看起来更像是测试客户端的阻塞问题。
如果你想等待来自服务器的结果,你需要在这种情况下让测试一直异步。
将测试方法转换为异步
[TestMethod]
public async Task test1()
//Arrange
Item item = new Item(...);
//Act
var preview = await Method2(item);
//Assert
Assert.IsNotNull(preview);
并更新方法以不混合异步和阻塞调用。
Method1
不需要asyn/await
,如果它在调用后没有使用任务,因此可以将其删除,只需让方法返回可以等待的Task
TestServer _owinTestServer;
public Task<HttpResponse> Method1(string url, object body)
return _owinTestServer.HttpClient.PostAsJsonAsync(url, body);
Method2
需要await
来自Method1
的响应,然后获取其内容。
public async Task<ItemPreview> Method2(object body)
var response = await Method1("..", body );
return await response.Content.ReadAsAsync<ItemPreview>();
【讨论】:
【参考方案2】:控制器方法是否同步并不重要。这纯粹是对服务器代码的关注。由于PostAsJsonAsync
方法是异步的,所以需要await
它:
var response = await owin.HttpClient.PostAsJsonAsync(uri, body);
这将允许您的代码等待来自服务器的响应。
【讨论】:
【参考方案3】:我假设您的初始代码看起来像这样......
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var result = response.Wait(15000);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
...
是的,等待 15 秒后结果将是错误的。这是因为您已经设置了请求,即 A.K.A 响应,但您实际上并没有进行调用,并且任何响应。Wait(n) 将返回 false。
您只需要启动 ReadAsAsync...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
但是,我想你会发现你可以一起跳过 response.Wait(n),因为它会返回 true,因为 ReadAsAsync() 将等待返回或失败。如果您希望配置请求超时,您可以在 HttpClient 中执行此操作,并且您的 ReadAsAsync 将抛出一个带有 TaskCanceledException 的 InnerException 的 AggregateException。
附带说明,您不需要使用 owin.HttpClient,您只需实例化一个新的 HttpClient。我相信您所指的 owin 对象是用于自托管您的 WebApi,我不知道这是否重要。但是假设您在 WebApi 上调用 Add(Item item) 并且 db.AddItem(item) 将返回 ItemPreview 对象,您的代码可能如下所示:
[TestMethod]
public void test1()
Item item = new(...);
var uri = "..";
var client = new HttpClient();
var response = client.PostAsJsonAsync(uri, item);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
/* The things to happen once you have item preview */
【讨论】:
【参考方案4】:结果可能始终为 false,因为 _db.AddItem 始终返回 false。如果没有,我已经对您的代码进行了理想的更改,这应该适合您
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
return await _owinTestServer.HttpClient.PostAsJsonAsync(url,body);
public async Task<ItemPreview> Method2(object body);
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
[TestMethod]
public void test1()
Item item = new(...);
await Method2(item).ContinueWith(task => // Never reach here
由于 Method2 返回一个异步任务,ContinueWith 不会等待它完成,因此它可能需要 await 方法调用。
【讨论】:
以上是关于Task.Wait 总是返回 false 虽然任务完成的主要内容,如果未能解决你的问题,请参考以下文章