Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么延续会以不可预测的顺序执行?_C#_.net_Asynchronous_Task Parallel Library_Task - Fatal编程技术网

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

C# 为什么延续会以不可预测的顺序执行?,c#,.net,asynchronous,task-parallel-library,task,C#,.net,Asynchronous,Task Parallel Library,Task,如果我把continuations链接在一起,它们似乎是按照我没有预料到的顺序执行的 例如: for (int i = 1; i < 6; i++) { HttpRequestMessage request = new HttpRequestMessage(); Task<JsonResult<MyResult>> message = Task.Factory.StartNew<HttpResponseMessage>(() =>

如果我把continuations链接在一起,它们似乎是按照我没有预料到的顺序执行的

例如:

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

}
for(int i=1;i<6;i++)
{
HttpRequestMessage请求=新建HttpRequestMessage();
任务消息=Task.Factory.StartNew(()=>
{
Console.WriteLine(i+“已启动”);
返回client.sendaync(request.Result);
})
.ContinueWith((r)=>
{
var stringresult=r.Result.Content.ReadAsStringAsync().Result;
var deserialized=JsonConvert.DeserializeObject(stringresult);
Console.WriteLine(反序列化的.Id+“反序列化的”);
返回反序列化;
})
.ContinueWith(m=>
{
控制台写入线(m.Id+“已完成”);
返回m.Result;
});
}
现在,我希望看到不同的请求交错,我会这样做,但我希望看到每个单独的进程按顺序执行:启动、反序列化、完成。但是,有时在反序列化继续之前执行“已完成”继续,如下所示:

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

我错过了什么

编辑:


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

正如@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;
            });
Task message=Task.Factory.StartNew(状态=>{
Console.WriteLine(state.ToString()+“started”);
返回client.sendaync(request.Result);
},i)
.ContinueWith((r)=>
{
var stringresult=r.Result.Content.ReadAsStringAsync().Result;
var deserialized=JsonConvert.DeserializeObject(stringresult);
Console.WriteLine(反序列化的.Id+“反序列化的”);
返回反序列化;
})
.ContinueWith(m=>
{
控制台写入线(m.Id+“已完成”);
返回m.Result;
});

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

        .ContinueWith<JsonResult<MyResult>>(m =>
        {
            Console.WriteLine(m.Result.Id + " completed");
            return m.Result;
        });
.ContinueWith(m=>
{
Console.WriteLine(m.Result.Id+“已完成”);
返回m.Result;
});

因此,您至少可以在continuations中比较相同的id。

因此,您最大的问题是,您正在对所有异步操作(
sendsync
ReadAsStringAsync
)执行同步等待。您只是在后台线程中执行这些同步等待。使用调用的
continuew没有任何价值,因为所有continuation都没有异步组件。您也可以将整个代码放在原始的
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;
        });
}
for(int i=1;i<6;i++)
{
HttpRequestMessage请求=新建HttpRequestMessage();
int id=i;
WriteLine(“{0}已启动”,id);
var result=client.sendsync(请求)
.ContinueWith(t=>
{
Console.WriteLine(“{0}正在读取”,id);
返回t.Result.Content.ReadAsStringAsync();
})
.Unwrap()
.ContinueWith(t=>
{
Console.WriteLine(“{0}read”,id);
var deserialized=JsonConvert.DeserializeObject(t.Result);
WriteLine(“{0}反序列化”,id);
返回反序列化;
});
}

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

这是您的真实代码吗?我这样问是因为你犯了一个普遍的错误,关闭了循环变量。这可能导致多次打印“5已启动”。我怀疑Console.WriteLine()中存在缓冲/锁定。尝试添加秒表并打印Stopwatch.ElapsedTicks,以查看它是否真的按照您的想法运行。您的代码与结果不匹配。e、 g.代码中没有打印长度。此外,打印的id:s也乱七八糟。第一个打印循环索引,第二个打印反序列化中的内容,第三个打印任务id。您没有比较相同的内容,因为您显示的代码在其
WriteLine
调用中不包含
长度,在匿名化代码时,您可能掩盖了实际问题。您似乎还没有掌握异步编程的意图。您正在对所有异步调用执行阻塞等待。使用您正在使用的延续没有任何附加值。这是一个完全同步的代码,看起来是异步的。我知道闭包,它已经处理好了。这个问题仍然存在