等待一个长期存在的任务以知道它在没有阻塞的情况下开始

Await a long-lived Task to know it started without blocking

我很好奇社区对 运行 长期后台任务(在我的例子中是 HTTP 服务器)的最佳实践的感觉(知道)是什么。理想情况下,我希望能够等待 Start 方法,但不希望它阻止进一步执行。

看来我有两个选择:1) await(在该语句执行后什么也不做),或 2) 将 Task 分配给消费代码中的变量。

  1. 等待(即“等待连接”从不显示)
class Program
{
    static HttpListener _Listener = null;
    static string _ListenerPrefix = "http://localhost:8888/";
    static Task _AcceptConnectionsTask = null;

    static async Task Main(string[] args)
    {
        _Listener = new HttpListener();
        _Listener.Prefixes.Add(_ListenerPrefix);

        Console.WriteLine("Starting server on " + _ListenerPrefix);
        await Start();
        Console.WriteLine("Waiting for connections");
    }

    static Task Start()
    {
        _Listener.Start();
        _AcceptConnectionsTask = Task.Run(() => AcceptConnections());
        return _AcceptConnectionsTask;
    }

    static async Task AcceptConnections()
    {
        while (true)
        {
            HttpListenerContext ctx = await _Listener.GetContextAsync()
                .ConfigureAwait(false);
            await Task.Run(() => HandleConnection(ctx));
        }
    }

    static async void HandleConnection(HttpListenerContext ctx)
    {
        string ip = ctx.Request.RemoteEndPoint.Address.ToString();
        int port = ctx.Request.RemoteEndPoint.Port;
        Console.WriteLine("Request received from " + ip + ":" + port + " "
            + ctx.Request.HttpMethod + " " + ctx.Request.Url);
        
        // do stuff and respond...
    }
}
  1. 分配给任务变量(显示“等待连接”)
static async Task Main(string[] args)
{
    _Listener = new HttpListener();
    _Listener.Prefixes.Add(_ListenerPrefix);

    Console.WriteLine("Starting server on " + _ListenerPrefix);
    // await Start();
    Task t = Start();
    Console.WriteLine("Waiting for connections");
    Console.ReadLine();
}

我的问题是,有没有一种方法可以等待,以便我知道任务已启动而不排除紧随其后的语句的执行?

问题是 await Start() 不会阻塞。它异步等待任务完成而不阻塞。

“等待连接”消息应该在长 运行 任务完成之前打印到控制台,例如在您调用 GetContextAsync() 异步等待请求之前。

此外,您应该删除对 Task.Run 的调用,让程序“一直异步”而不涉及任何后台线程。像这样:

class Program
{
    static HttpListener _Listener = null;
    static string _ListenerPrefix = "http://localhost:8888/";

    static async Task Main(string[] args)
    {
        _Listener = new HttpListener();
        _Listener.Prefixes.Add(_ListenerPrefix);

        Console.WriteLine("Starting server on " + _ListenerPrefix);
        await Start();
    }

    static async Task Start()
    {
        _Listener.Start();
        await AcceptConnections().ConfigureAwait(false);
    }

    static async Task AcceptConnections()
    {
        Console.WriteLine("Waiting for connections");
        while (true)
        {
            HttpListenerContext ctx = await _Listener.GetContextAsync().ConfigureAwait(false);
            HandleConnection(ctx);
        }
    }

    static void HandleConnection(HttpListenerContext ctx)
    {
        string ip = ctx.Request.RemoteEndPoint.Address.ToString();
        int port = ctx.Request.RemoteEndPoint.Port;
        Console.WriteLine("Request received from " + ip + ":" + port + " " + ctx.Request.HttpMethod + " " + ctx.Request.Url);

        // do stuff and respond...
    }
}

您可能想要做的是将任务的创建与等待分开:

static async Task Main(string[] args)
{
    //...
    Console.WriteLine("Starting server on " + _ListenerPrefix);
    Task t = Start();
    Console.WriteLine("Waiting for connections (the task has been started)");
    await t;
    Console.WriteLine("The task has been completed");
}