1

如果我将延续链接在一起,它们似乎是以我没想到的顺序执行的。

例如:

for (int i = 1; i < 6; i++)
{
    HttpRequestMessage request = new HttpRequestMessage();

    Task<JsonResult<MyResult>> message = Task.Factory.StartNew<HttpResponseMessage>(() =>
        {
            Console.WriteLine(i + " started");
            return client.SendAsync(request).Result;
        })
        .ContinueWith<JsonResult<MyResult>>((r) =>
            {
                var stringresult = r.Result.Content.ReadAsStringAsync().Result;
                var deserialized = JsonConvert.DeserializeObject<JsonResult<MyResult>>(stringresult);
                Console.WriteLine(deserialized.Id + " deserialized");
                return deserialized;
            })
        .ContinueWith<JsonResult<MyResult>>(m =>
            {
                Console.WriteLine(m.Id + " completed");
                return m.Result;
            });

}

现在我希望看到交错的不同请求,我确实这样做了,但我希望看到每个单独的进程按顺序执行:开始、反序列化、完成。但是,有时“完成”的继续在反序列化继续之前执行,如下所示:

1 开始
2 开始
3 开始
4 开始
1 反序列化,长度:69
1 完成
5 开始
5 反序列化,长度:831
2 完成
4 反序列化,长度:1022
3 完成
3 反序列化,长度:356
4 完成
2 反序列化,长度:878
5 完成

我在这里想念什么?

编辑:

是的,我知道闭包,真正的代码要长得多,当然会处理这个,但是我去掉了我认为与问题无关的废话,然后每个人都去关注丢失的废话!它不会改变我看到的问题或问题。

4

3 回答 3

0

因此,您最大的问题是您正在对所有异步操作(SendAsyncReadAsStringAsync)进行同步等待。您只是在后台线程中进行这些同步等待。您的调用根本没有任何价值ContinueWith,因为您的所有延续都没有异步组件。您不妨将整个代码放在原始StartWith调用中;把它放在一个延续中不会给你带来任何好处。

这是您的程序示例,但没有异步操作的同步等待:

for (int i = 1; i < 6; i++)
{
    HttpRequestMessage request = new HttpRequestMessage();

    int id = i;
    Console.WriteLine("{0} started", id);
    var result = client.SendAsync(request)
        .ContinueWith(t =>
        {
            Console.WriteLine("{0} reading", id);
            return t.Result.Content.ReadAsStringAsync();
        })
        .Unwrap()
        .ContinueWith(t =>
        {
            Console.WriteLine("{0} read", id);
            var deserialized = JsonConvert.DeserializeObject<JsonResult<MyResult>>(t.Result);
            Console.WriteLine("{0} deserialized", id);
            return deserialized;
        });
}

另请注意,您向我们展示的代码中的一个问题是您在登录控制台时并未始终使用相同的标识号。我一直使用循环变量,以保持清晰。我还确保获取循环变量的副本,这样我就不会关闭循环变量。我还记录了稍微不同的东西,这些东西应该会产生更有趣的结果。

于 2013-02-18T15:53:50.960 回答
0

正如@Henrik 所说,当线程运行时,值i将会改变。这将使确定线程实际运行的顺序看起来不正确。

尝试将iin 传递给函数:

  Task<JsonResult<MyResult>> message = Task.Factory.StartNew<HttpRequestMessage>(state =>  {

            Console.WriteLine(state.ToString() + " started");
            return client.SendAsync(request).Result;
        }, i )
            .ContinueWith<JsonResult<MyResult>>((r) =>
            {
                var stringresult = r.Result.Content.ReadAsStringAsync().Result;
                var deserialized = JsonConvert.DeserializeObject<JsonResult<MyResult>>(stringresult);
                Console.WriteLine(deserialized.Id + " deserialized");
                return deserialized;
            })
            .ContinueWith<JsonResult<MyResult>>(m =>
            {
                Console.WriteLine(m.Id + " completed");
                return m.Result;
            });
于 2013-02-18T15:28:31.217 回答
0

您没有在打印输出中比较相同的 id:s。尝试将最后一个延续更改为

        .ContinueWith<JsonResult<MyResult>>(m =>
        {
            Console.WriteLine(m.Result.Id + " completed");
            return m.Result;
        });

所以你至少在延续中比较相同的 id。

于 2013-02-18T15:32:56.650 回答