等待TaskEx.Delay

3

我正在使用HttpListener和Async CTP进行开发

class HttpServer : IDisposable
{
    HttpListener listener;
    CancellationTokenSource cts;

    public void Start()
    {
        listener = new HttpListener();
        listener.Prefixes.Add("http://+:1288/test/");
        listener.Start();

        cts = new CancellationTokenSource();

        Listen();
    }

    public void Stop()
    {
        cts.Cancel();
    }

    int counter = 0;

    private async void Listen()
    {
        while (!cts.IsCancellationRequested)
        {
            HttpListenerContext context = await listener.GetContextAsyncTask(); // my extension method with TaskCompletionSource and BeginGetContext
            Console.WriteLine("Client connected " + ++counter);

            // simulate long network i/o
            await TaskEx.Delay(5000, cts.Token);

            Console.WriteLine("Response " + counter);
            // send response
        }

        listener.Close();
    }
}

我希望当3个客户端同时连接时,能够得到以下输出结果。
Client connected 1
Client connected 2
Client connected 3
<5000ms pause>
Response 1
Response 2
Response 3

相反,我得到
Client connected 1
<5000ms pause>
Response 1
Client connected 2
<5000ms pause>
Response 2
Client connected 3
<5000ms pause>
Response 3

如果我使用 continuation,它就像我预期的那样工作。

int c = counter;
TaskEx.Delay(5000, cts.Token).ContinueWith(t => {
    Console.WriteLine("Response " + c);
    // send response
});

我曾经认为await TaskEx.Delay会立即返回(并进入while (!cts.IsCancellationRequested)),而while块的剩余部分将在5000ms后成为延续。因此,它应该与我的代码中的.ContinueWith相同,对吗?

2个回答

5

TaskEx.Delay将立即返回一个任务,但在调用listener.GetContextAsyncTask()之前,您正在等待该任务。只要您遇到第一个尚未完成的await语句,调用者Start)就会继续执行,但是在Listen方法内部,它具有同步代码的外观 - 这就是await目的

当延续触发时,不是在词法上发生“块的其余部分”,而是在方法的执行中发生。换句话说,在await处方法实际上“暂停”,但允许调用者继续进行。


本能地等待代码块的其余部分确实是有意义的。这是否在技术上也是有意义的呢? - stevenrcfox
@Overflow:想象一下这个块是一个循环,而且在中间有一个await...当你到达块的末尾时,你会回到await表达式之前。这有助于回答你的问题吗?我不确定我是否理解了... - Jon Skeet

1

我找到了实现我想做的事情的方法(即使用await而不是.ContinueWith和委托):

private async void Listen()
{
    while (!cts.IsCancellationRequested)
    {
        HttpListenerContext context = await listener.GetContextAsyncTask();
        Console.WriteLine("Client connected " + ++counter);

        ProcessRequest(context, counter);
    }

    listener.Close();
}

private async void ProcessRequest(HttpListenerContext context, int c)
{
    await TaskEx.Delay(5000, cts.Token);
    Console.WriteLine("Response " + c);
}

是的,这个方法能够运行是因为Jon上面所说的“方法执行的剩余部分”。 - stevenrcfox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接