1

我正在尝试创建一个自托管的 Web 服务,该服务启动它的服务器Microsoft.Owin.Hosting.WebApp.Start<NotifyStartup>("http://localhost:9000/")并包含一个从System.Net.Http.ApiController. NotifyStartup看起来像这样:

using System.Web.Http;
using Owin;
...
class NotifyStartup
{
    public void Configuration(IAppBuilder appBuilder)
    {
        var config = new HttpConfiguration();
        config.MapHttpAttributeRoutes();

        appBuilder.UseWebApi(config);
    }
}

控制器有这个 URI 处理程序:

using System.Web.Http;
...
[Route("notify")]
[HttpPost]
public IHttpActionResult Notify([FromBody]object body)
{
    return new HttpAction();
}

这是HttpAction:

using System.Net.Http;
using System.Web.Http;
using System.Threading;
using System.Threading.Tasks;
...
public class HttpAction : IHttpActionResult
{
    public HttpAction() { }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return new Task<HttpResponseMessage>(() =>
        {
            var rspContent = "here's a response string";
            var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
            if (!string.IsNullOrEmpty(rspContent))
            {
                rsp.Content = new StringContent(rspContent);
            }
            return rsp;
        }, cancellationToken);
    }
}

(在某些时候HttpAction会注意Notify()'sbody参数,并 rspContent会分配一些需要一些数据库查找的东西,这就是为什么我试图使这项工作异步进行。)

当我运行程序并 POST 到http://localhost:9000/notify/时,会调用 URI 处理程序,它会创建一个HttpAction实例并ExecuteAsync()调用该实例的方法。但是,它返回的任务永远不会运行,客户端挂起等待响应。如果我更改ExecuteAsync()以便同步完成工作并在包装器任务中返回响应:

var rspContent = "here's a response string";
var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
if (!string.IsNullOrEmpty(rspContent))
{
    rsp.Content = new StringContent(rspContent);
}
return Task.FromResult(rsp);

该包装器任务运行并且客户端收到它的响应。

据我所知,由调用者创建的任务看起来应该new Task<>...Task.FromResult()调用者相同。为什么它await(或它实际为获得结果所做的任何事情)是一个而不是另一个?我究竟做错了什么?有可能完成这项工作吗?

4

1 回答 1

0

由调用者创建的任务看起来应该new Task<>Task.FromResult()调用者相同。

从调用者的角度来看,它们看起来确实相同,但从实现的角度来看,它们并不相同。

构造Task函数不会启动任务,这就是你不应该使用它的原因。而是使用Task.Runwhich 返回一个热任务:

public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
    return Task.Run(() =>
    {
        var rspContent = "here's a response string";
        var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
        if (!string.IsNullOrEmpty(rspContent))
        {
            rsp.Content = new StringContent(rspContent);
        }
        return rsp;
    }, cancellationToken);
}

尽管我认为这本身可能是多余的,但由于 WebAPI 中的操作本身已经在线程池线程上运行,因此将其包装在附加线程中通常是多余的。

于 2017-01-24T05:16:11.007 回答