C# 不同的任务方法工作方式不同

C# 不同的任务方法工作方式不同,c#,.net,multithreading,asynchronous,task,C#,.net,Multithreading,Asynchronous,Task,我想问一下,为什么这两种方法不同,并返回两组不同的值: 我认为第一个是正确的,它返回0到8之间的值,并在不同的线程上工作(LINQPad代码): 第二,在我看来这是不正确的,它返回的随机值是9,但也适用于不同的线程 void Main() { var tasks = new List<Task<int>>(); for (var index = 0; index < 9; index++) { var task =

我想问一下,为什么这两种方法不同,并返回两组不同的值:

我认为第一个是正确的,它返回0到8之间的值,并在不同的线程上工作(LINQPad代码):

第二,在我看来这是不正确的,它返回的随机值是9,但也适用于不同的线程

void Main()
{
    var tasks = new List<Task<int>>();
    
    for (var index = 0; index < 9; index++)
    {
        var task = Task.Run(() => DoSomething(index));
        
        tasks.Add(task);
    }
    
    Task.WaitAll(tasks.ToArray());
}    

public int DoSomething(int value)
{
    return value;
}
void Main()
{
var tasks=新列表();
对于(var指数=0;指数<9;指数++)
{
var task=task.Run(()=>DoSomething(index));
任务。添加(任务);
}
Task.WaitAll(tasks.ToArray());
}    
公共整数剂量测量(整数值)
{
返回值;
}
是否可以修改第二个,得到与第一个示例类似的结果

谢谢你的回答

是否可以修改第二个,得到与第一个示例类似的结果

当您将变量或对象传递到
Task.Run(()=>DoSomething(index))
中时,您将捕获一个变量

从某种意义上说,您不仅仅是将该变量作为值或对象发送,而是让该线程直接访问原始变量。(这是高度概括的,有很多细微差别)

您最可能遇到的问题是CLR试图捕获
索引
,这是一个迭代器,它的作用域不能离开它的边界,除非任何这样做的尝试都会保留在堆栈上(基本上是同一个线程,也是过度泛化的)

当CLR“捕获”
index
时,它将只捕获
index
是(由于种族条件)的随机值,或
index
是的最后一个值,这是非直观的,因为您明确告诉它
index
永远不会到达
9
(var index=0;index<9;index++)

这里有一件棘手的事情,当CLR捕获
index
时,它捕获
index
的最后一个值。但是循环结束后,
index
再次递增,导致
9
成为
index
的最后一个值

这可能会导致一系列问题,特别是当您使用数组时,我们来看看
IndexOutOfBoundsException

如何修复它
修复实际上非常简单!只需在循环中创建一个临时变量,并传递它,而不是直接传递
索引

下面是一个例子:

void Main()
{
var tasks=新列表();
对于(var指数=0;指数<9;指数++)
{
int tmpIndex=索引;
var task=task.Run(()=>DoSomething(tmpIndex));
任务。添加(任务);
}
Task.WaitAll(tasks.ToArray());
}
是否可以修改第二个,得到与第一个示例类似的结果

旁注
切勿依赖任务中的数据来保持有序,除非您明确实施了保证项目恢复有序的措施。
TaskScheduler
选择
tasks
FIFO中正常运行(先进先出),但是,如果
TaskScheduler
对任务进行优化、优先排序或转移(这是经常发生的)你的任务不会按顺序完成,随后你得到的结果也不会按顺序完成。

相关:
void Main()
{
    var tasks = new List<Task<int>>();
    
    for (var index = 0; index < 9; index++)
    {
        var task = Task.Run(() => DoSomething(index));
        
        tasks.Add(task);
    }
    
    Task.WaitAll(tasks.ToArray());
}    

public int DoSomething(int value)
{
    return value;
}