为什么延续以不可预测的顺序执行?

时间:2013-02-18 14:40:58

标签: c# .net asynchronous task-parallel-library task

如果我将连续组合在一起,它们似乎按照我没想到的顺序执行。

例如:

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完成

我在这里缺少什么?

修改

是的,我知道关闭,真正的代码更长,当然处理这个,但我剥离了我认为与问题无关的guff,然后每个人都去关注失踪废话!!它不会改变我所看到的问题或问题。

3 个答案:

答案 0 :(得分:0)

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

尝试将i传递给函数:

  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;
            });

答案 1 :(得分:0)

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

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

所以你至少要比较续集中的相同ID。

答案 2 :(得分: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;
        });
}

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