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 虽然任务完成的主要内容,如果未能解决你的问题,请参考以下文章

Task用法-任务等待wait

使用Task.Wait而不是等待异步编程

27.5 任务

为啥我的线性搜索总是返回 false,即使该项目在数组中?

JS Array.every 在比较字符串时总是返回 false

C#使用异步操作时的注意要点(翻译)