Иногда в циклах, используемых в программах, итерации являются независимыми; т.е., ни в одной итерации не используются результаты работы предыдущих итераций. Посредством класса System. такие циклы могут быть преобразованы в циклы, в которых каждая итерация потенциально может быть выполнена параллельно при наличии достаточного количества доступных ядер (процессоров). Заметим, что параллельное исполнение циклов, итерации которых не являются независимыми, может привести к некорректным результатам. В этом случае необходимо либо пересмотреть структуру цикла, либо использовать примитивы синхронизации и\или потокобезопасные структуры данных.
В PFX существует несколько вариантов метода For, исполняющихся параллельно. Наиболее часто применяемым является For(Int32, Int32, Action<Int32>). Здесь первый параметр задает начальный индекс, второй параметр - конечный индекс, и третий параметр -
Рассмотрим следующий последовательный цикл на С#:
for (int i = 0; i < N; i++)
{
results[i] = Compute(i);
}
C помощью PFX и класса System.
using System.Threading;
…
Parallel.For(0, N, delegate(int i)
{
results[i] = Compute(i);
});
Для упрощения кода можно использовать синтаксис лямбда-выражений, введенный в C# 3.0 (Visual Studio 2008):
using System.Threading;
…
Parallel.For(0, N, i =>
{
results[i] = Compute(i);
});
Теперь итерации этого цикла могут быть выполнены параллельно.
foreach(MyClass с in data)
{
Compute(c);
}
C помощью PFX он распараллеливается следующим образом:
Parallel.Foreach(data, delegate(MyClass с))
{
Compute(c);
}
Упрощенный код с использованием лямбда-выражений выглядит так:
Parallel.Foreach(data, с =>
{
Compute(c);
});
Использование foreach, как правило, менее эффективно, чем for, поскольку несколько потоков должны иметь доступ к общей обрабатываемой структуре данных, например, списку. Однако, реализация
В качестве примера использования
void MultiplyMatrices(int size, double[,] m1, double[,] m2, double[,] result)
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
result[i,j] = 0;
for (int k = 0; k < size; k++)
{
result[i,j] += m1[i,k] * m2[k,j];
}
}
}
}
void MultiplyMatrices(int size, double[,] m1, double[,] m2, double[,] result)
{
Parallel.For (0; size; i =>
{
for (int j = 0; j < size; j++)
{
result[i,j] = 0;
for (int k = 0; k < size; k++)
{
result[i,j] += m1[i,k] * m2[k,j];
}
}
});
}
Также можно выполнить
Еще один пример применения
foreach(string imagePath in Directory.GetFiles(path, "*.jpg"))
{
ProcessImage(imagePath);
}
Параллельный вариант выглядит следующим образом:
Parallel.ForEach(Directory.GetFiles(path, "*.jpg"), imagePath =>
{
ProcessImage(imagePath);
});
Конструкции имеют несколько перегруженных ( каждый рабочий поток выполняет, в общем случае, несколько итераций. Например, при общем количестве итераций 100, на 4-хядерной машине, обычно будет запущено 4 потока, каждый из которых выполнит 25 итераций. Обычно, передача информации (например, подсчет какого-либо значения) между итерациями, исполняющимися в одном потоке, нужна для вычисления некоторого сводного значения по всем итерациям цикла.
Одним из вариантов перегруженной конструкции для поддержки вычислений такого рода выглядит так:
public static void For<TLocal> (
int fromInclusive, int toExclusive,
Func<TLocal> threadLocalInit,
Action<int, ParallelState<TLocal> > body,
Action<TLocal> threadLocalFinally);
Здесь тип TLocal задает тип объекта, с которым будут работать итерации в рамках одного потока. threadLocalInit вызывается один раз для каждого потока, выделенного для исполнения группы итераций цикла, перед тем как этот поток начал исполнять свои итерации. Обычно с помощью этого делегата создается или инициализируется объект типа TLocal с которыми будут работать итерации.
Результат каждой итерации может быть записан в объект класса ParallelState<TLocal> который является одним из body. body может обновлять специальное поле (а точнее, свойство) ThreadLocalState, которое имеет объект ParallelState<TLocal>, и которое имеет тип TLocal, и значение этого поля будет доступно следующим итерациям, исполняющимся в данном потоке. После того, как поток завершил исполнение своих итераций, в рамках его же, будет вызван threadLocalFinally, которому в качестве параметра будет передано значение ThreadLocalState.
Примером использования одного из перегруженных вариантов конструкции может служить следующий код:
Parallel.For(0, N, () => new NonThreadSafeData(), (i,loop)= >
{
UseData(loop.ThreadLocalState);
});
(Здесь не использован threadLocalFinally из вышеприведенной
В данном примере для каждого нового потока в рамках будет создан свой объект класса NonThreadSafeData (т.е., в данном случае TLocal = NonThreadSafeData ). Таким образом, объект NonThreadSafeData будет передаваться между итерациями одного потока, что позволит потоку безопасно использовать этот объект. При этом, таких объектов будет создано ровно столько, сколько потоков будет запущено. Отметим также, что ParallelState<TLocal> .
Полный пример вычисления некоторого сводного значения по совокупности данных с использованием объектов-состояний приведен ниже. Такое использование локальных по отношению к threadLocalFinally, используя блокирующую конструкцию:
int total = 0;
Parallel.ForEach(data, ()=>0, (elem,i,loop)= >
{
loop.ThreadLocalState += Process(elem);
},
partial => Interlocked.Add(ref total, partial));
Отметим, что в данном случае partial имеет тип TLocal, который, в данном примере, есть тип $$int$$ (см. threadLocalInit ).
Если требуется передавать между итерациями несколько общих переменных, то, как обычно делают в таких случаях, можно создать собственный класс из этих переменных, и уже его передавать как ThreadLocalState:
class MultipleValues { public int Total, Count; }
int total = 0, count = 0;
Parallel.ForEach(data,
()=>new MultipleValues { Total=0, Count=0 },
(elem,i,loop)= >
{
loop.ThreadLocalState.Total += Process(elem);
loop.ThreadLocalState.Count++;
},
partial => {
Interlocked.Add(ref total, partial.Total);
Interlocked.Add(ref count, partial.Count);
});
Задания:
Реализуйте подсчет суммы элементов массива с использованием предлагаемого подхода, кроме того, подсчитайте, сколько потоков было запущено при исполнении в вашей программе.
Изучите понятие "анонимный класс" языка C# и выясните можно ли использовать анонимные классы вместо явного определения класса MultipleValues в приведенном выше примере,и если нельзя, то почему.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.