不同的任务方法以不同的方式工作
Different Task approaches work differently
我想问一下为什么这两种方法不同并且return有两组不同的值:
第一个在我看来是正确的 return 值从 0 到 8 并且适用于不同的线程(LINQPad 代码):
void Main()
{
var newTasks = Enumerable.Range(0, 9).Select(x => Task.Run(() => DoSomething(x)));
Task.WhenAll(newTasks);
}
public int DoSomething(int value)
{
return value;
}
其次,在我看来这是不正确的 returns 随机值特别是 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;
}
是否可以修改第二个并得到与第一个示例类似的结果?
感谢您的回答。
Is it possible to modify the second one and get the result similar to first example?
当您将变量或对象传递给 Task.Run(()=> DoSomething(index))
时,您 捕获 一个变量。
从某种意义上说,您不只是将该变量作为值或对象发送,而是让该线程直接访问原始变量。 (这是高度概括的并且有很多细微差别)。
您最有可能遇到的问题是 CLR 正在尝试捕获 index
,这是一个 迭代器,其范围不能离开它的边界,除非有任何尝试这样做会保留在堆栈上(基本上是同一个线程,再次泛化)。
当 CLR 'captures' index
时,它只会捕获 index
的随机值(由于竞争条件) - 或者 last 是 index
的值,这是 double 不直观,因为你明确告诉它 index
不应该在这一行达到 9
这里 for (var index = 0; index < 9; index++)
.
这是一个棘手的事情,当 CLR 捕获 index
时,它会捕获 index
的最后一个值。但是在循环结束后 index
又增加了一次导致 9
成为 index
.
的最后一个值
这可能会导致一大堆问题,尤其是当您使用数组时,我们来了 IndexOutOfBoundsException
!
如何修复
修复实际上非常简单!只需在循环中创建一个临时变量,然后直接传递它而不是 index
。
这是一个例子:
void Main()
{
var tasks = new List<Task<int>>();
for (var index = 0; index < 9; index++)
{
int tmpIndex = index;
var task = Task.Run(() => DoSomething(tmpIndex ));
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
Is it possible to modify the second one and get the result similar to first example?
旁注
永远不要依赖任务中的数据是有序的,除非您明确实施了项目按顺序返回的保证。 TaskScheduler
通常在FIFO中选择Tasks
到运行(先进先出),但是,如果TaskScheduler
优化,优先,或转移任务(经常这样做)你的任务将不按顺序完成,随后你得到的结果将不按顺序。
我想问一下为什么这两种方法不同并且return有两组不同的值:
第一个在我看来是正确的 return 值从 0 到 8 并且适用于不同的线程(LINQPad 代码):
void Main()
{
var newTasks = Enumerable.Range(0, 9).Select(x => Task.Run(() => DoSomething(x)));
Task.WhenAll(newTasks);
}
public int DoSomething(int value)
{
return value;
}
其次,在我看来这是不正确的 returns 随机值特别是 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;
}
是否可以修改第二个并得到与第一个示例类似的结果?
感谢您的回答。
Is it possible to modify the second one and get the result similar to first example?
当您将变量或对象传递给 Task.Run(()=> DoSomething(index))
时,您 捕获 一个变量。
从某种意义上说,您不只是将该变量作为值或对象发送,而是让该线程直接访问原始变量。 (这是高度概括的并且有很多细微差别)。
您最有可能遇到的问题是 CLR 正在尝试捕获 index
,这是一个 迭代器,其范围不能离开它的边界,除非有任何尝试这样做会保留在堆栈上(基本上是同一个线程,再次泛化)。
当 CLR 'captures' index
时,它只会捕获 index
的随机值(由于竞争条件) - 或者 last 是 index
的值,这是 double 不直观,因为你明确告诉它 index
不应该在这一行达到 9
这里 for (var index = 0; index < 9; index++)
.
这是一个棘手的事情,当 CLR 捕获 index
时,它会捕获 index
的最后一个值。但是在循环结束后 index
又增加了一次导致 9
成为 index
.
这可能会导致一大堆问题,尤其是当您使用数组时,我们来了 IndexOutOfBoundsException
!
如何修复
修复实际上非常简单!只需在循环中创建一个临时变量,然后直接传递它而不是 index
。
这是一个例子:
void Main()
{
var tasks = new List<Task<int>>();
for (var index = 0; index < 9; index++)
{
int tmpIndex = index;
var task = Task.Run(() => DoSomething(tmpIndex ));
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
Is it possible to modify the second one and get the result similar to first example?
旁注
永远不要依赖任务中的数据是有序的,除非您明确实施了项目按顺序返回的保证。 TaskScheduler
通常在FIFO中选择Tasks
到运行(先进先出),但是,如果TaskScheduler
优化,优先,或转移任务(经常这样做)你的任务将不按顺序完成,随后你得到的结果将不按顺序。