При подготовке к экзамену по 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, представляющий работу.
Но правильный ответ был 1, используя конструктор Task (Action, Object) .В объяснении говорится следующее:
В ответе 1 второй аргумент конструктору передается как единственный аргумент делегату Action.Текущее значение переменной i фиксируется, когда значение упаковывается в коробку и передается в конструктор Task.
В ответах 2 и 3 используется лямбда-выражение, которое захватывает переменную i из включающего метода.Лямбда-выражение, вероятно, вернет окончательное значение i, в данном случае 10, прежде чем операционная система вытеснит текущий поток и начнет каждый делегат задачи, созданный циклом.Точное значение не может быть определено, потому что ОС планирует выполнение потока на основе многих факторов, внешних по отношению к вашей программе.
Хотя я прекрасно понимаю объяснение ответа 1, я не понимаю смысла в объясненияхдля ответов 2 и 3. Почему лямбда-выражение возвращает окончательное значение?