Это означает, что указанная переменная не указана ни на что. Я мог бы сгенерировать это так:
SqlConnection connection = null;
connection.Open();
Это вызовет ошибку, потому что, пока я объявил переменную «connection
», она не указала ни на что. Когда я пытаюсь вызвать член «Open
», для его устранения нет ссылки, и он будет вызывать ошибку.
Чтобы избежать этой ошибки:
object == null
. Инструмент Resharper JetBrains определит каждое место в вашем коде, которое имеет возможность ошибки нулевой ссылки, позволяя вам ввести нулевую проверку. Эта ошибка является источником ошибок номер один, IMHO.
Первый вариант гораздо лучше.
Parallel.ForEach, внутренне, использует Partitioner<T>
для распространения вашей коллекции на рабочие элементы. Он не будет выполнять одну задачу для каждого элемента, а скорее сделает это, чтобы снизить задействованные служебные данные.
Второй вариант запланирует один Task
на элемент в вашей коллекции. Хотя результаты будут (почти) одинаковыми, это принесет гораздо больше накладных расходов, чем необходимо, особенно для больших коллекций, и приведет к замедлению общего времени выполнения.
FYI. Используемый разделитель можно контролировать с помощью используя соответствующие перегрузки для Parallel.ForEach , если это необходимо. Подробнее см. Пользовательские разделители в MSDN.
Основное различие во время выполнения - это второе действие будет асинхронным. Это можно продублировать с помощью Parallel.ForEach, выполнив:
Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));
Поступая таким образом, вы по-прежнему пользуетесь разделителями, но не блокируете до завершения операции.
Я сделал небольшой эксперимент по запуску метода «1000000000» раз с «Parallel.For» и одним с объектами «Задача».
Я измерил время процессора и нашел Parallel более эффективным. Parallel.For делит вашу задачу на небольшие рабочие элементы и выполняет их на всех ядрах в оптимальном порядке. При создании множества объектов задач (FYI TPL будет использовать пул потоков внутри) будет перемещать каждое выполнение по каждой задаче, создавая больше стресса в ящике, что видно из приведенного ниже эксперимента.
Я также создал небольшое видео, которое объясняет базовый TPL, а также продемонстрировало, как Parallel.For использует ваше ядро более эффективно http://www.youtube.com/watch?v=No7QqSc5cl8 по сравнению с обычными задачами и потоками.
Эксперимент 1
Parallel.For(0, 1000000000, x => Method1());
Эксперимент 2
for (int i = 0; i < 1000000000; i++)
{
Task o = new Task(Method1);
o.Start();
}
[/g1]
Parallel.ForEach будет оптимизировать (возможно, даже не запускать новые потоки) и блокировать до тех пор, пока цикл не будет завершен, а Task.Factory явно создаст новый экземпляр задачи для каждого элемента и вернется до их завершения (асинхронные задачи). Parallel.Foreach намного эффективнее.
На мой взгляд, самый реалистичный сценарий - когда задачи выполняют тяжелую операцию. Подход Шивпрасада больше фокусируется на создании объекта / распределении памяти, чем на самом вычислении. Я провел исследование, назвав следующий метод:
public static double SumRootN(int root)
{
double result = 0;
for (int i = 1; i < 10000000; i++)
{
result += Math.Exp(Math.Log(i) / root);
}
return result;
}
Выполнение этого метода занимает около 0,5 сек.
Я назвал его 200 раз, используя Parallel:
Parallel.For(0, 200, (int i) =>
{
SumRootN(10);
});
Затем я назвал его 200 раз, используя старомодный способ:
List<Task> tasks = new List<Task>() ;
for (int i = 0; i < loopCounter; i++)
{
Task t = new Task(() => SumRootN(10));
t.Start();
tasks.Add(t);
}
Task.WaitAll(tasks.ToArray());
Первый случай завершен в 26656 мсек, второй - в 24478мс. Я повторил это много раз. Каждый раз, когда второй подход маргиналист быстрее.