在大学准备C#考试时,我发现了以下多项选择题:
客户端应用程序通过传递一组操作来调用您的库 去表演。您的图书馆必须确保系统资源最多 有效使用。可以按任何顺序安排作业,但是您的 库必须记录每个操作的位置。您已经声明了 代码:
public IEnumerable<Task> Execute(Action[] jobs) { var tasks = new Task[jobs.Length]; for (var i = 0; i < jobs.Length; i++) { /* COMPLETION NEEDED */ } return tasks; } public void RunJob(Action job, int index) { // implementation omitted }
通过在for循环中插入代码来完成该方法。选择 正确答案。
1.) tasks[i] = new Task((idx) => RunJob(jobs[(int)idx], (int)idx), i); tasks[i].Start(); 2.) tasks[i] = new Task(() => RunJob(jobs[i], i)); tasks[i].Start(); 3.) tasks[i] = Task.Run(() => RunJob(jobs[i], i));
我选择了答案3,因为Task.Run()
将指定的工作在线程池中排队,并返回代表该工作的Task对象。
但是使用Task(Action, Object)构造函数,正确答案是1。说明如下:
在答案1中,构造函数的第二个参数作为 动作委托的唯一参数。当前值 将值装箱并传递给Task时,将捕获i变量 构造函数。
答案2和3使用可捕获i变量的lambda表达式 从封闭方法。 Lambda表达式可能会返回 i的最终值,在这种情况下为10,在操作系统之前 抢占当前线程并开始由创建的每个任务委托 循环。无法确定确切值,因为操作系统 根据您外部的许多因素安排线程执行 程序。
虽然我完全理解答案1的解释,但我对答案2和3的解释却不明白。为什么lambda表达式会返回最终值?
答案 0 :(得分:1)
在选项2和3中,lambda捕获i
循环中使用的原始for
变量。不能保证何时在线程池上运行任务。可能的行为是:for
循环完成,i=10
,然后开始执行任务。因此他们所有人都将使用i=10
。
类似的行为,您可以在这里看到:
void Do()
{
var actions = new List<Action>();
for (int i = 0; i < 3; i++)
{
actions.Add(() => Console.WriteLine(i));
}
//actions executed after loop is finished
foreach(var a in actions)
{
a();
}
}
输出为:
3
3
3
您可以这样解决它:
for (int i = 0; i < 3; i++)
{
var local = i;
actions.Add(() => Console.WriteLine(local));
}