|
не хватает одного параметра: static void Main(string[] args) |
Потоки и параллельные вычисления
Быстрая сортировка Хоора и потоки
Введение потоков в алгоритм быстрой сортировки позволит продемонстрировать еще один прием работы с потоками, отличный от применяемого при пузырьковой сортировке. Дело в том, что быстрая сортировка представляет рекурсивную процедуру, для которой передача параметров обязательна. По этой причине, метод, передаваемый потоку в момент его создания должен иметь параметры. Технология работы с потоками разрешает методу иметь параметры, но параметр должен быть только один универсального типа object.
Чтобы удовлетворить технологическим требованиям, введем структуру, описывающую параметры, требуемые методу сортировки:
struct StructOne
{
public double[] mas;
public int n;
public int start, finish;
public StructOne(double[] mas, int start, int finish)
{
this.mas = mas;
this.start = start;
this.finish = finish;
n = mas.Length;
}
}Рассмотрим теперь версию параллельного алгоритма, работающего с потоками:
public void QSortWithTreads(double[] mas, int p)
{
Thread[] threads = new Thread[p];
StructOne[] sorts = new StructOne[p];
int start = 0, finish = 0, n = mas.Length, m = n/p;
for (int i = 0; i < p; i++)
{
start = i * m;
finish = i != p - 1 ? start + m - 1 : n - 1;
sorts[i] = new StructOne(mas, start, finish);
threads[i] = new Thread(QSortStruc);
threads[i].Start(sorts[i]);
}
for (int i = 0; i < p; i++)
{
threads[i].Join();
}
//Слияние отсортированных последовательностей
MergeQ(mas, p);
}Как можно видеть, при создании потока ему не передается экземпляр некоторого класса. Всем потокам передается один и тот же метод QSortStruc, но параметр, передаваемый потоку, у каждого потока свой, что гарантирует корректное распараллеливание по данным.
Метод QSortStruc представляет собой слегка модифицированную версию классического алгоритма быстрой сортировки:
void QSortStruc(object param)
{
StructOne s = (StructOne)param;
double[] mas = s.mas;
int start = s.start;
int finish = s.finish;
int n = s.n;
if (finish - start > 0)
{
double cand = 0, temp = 0;
int l = start, r = finish;
cand = mas[(r + l) / 2];
while (l <= r)
{
while (mas[l] < cand) l++;
while (mas[r] > cand) r--;
if (l <= r)
{
temp = mas[l];
mas[l] = mas[r];
mas[r] = temp;
l++; r--;
}
}
StructOne left = new StructOne(mas, start, r);
QSortStruc(left);
StructOne right = new StructOne(mas, l, finish);
QSortStruc(right);
}
}Взглянем теперь на результаты численных экспериментов и посмотрим, что дает введение потоков в алгоритм быстрой сортировки:
Как видите, все три метода легко справляются с сортировкой достаточно больших массивов. Время сортировки практически сравнимо с величиной ошибки измерения времени. Введение потоков не дает никаких преимуществ. Классический последовательный алгоритм не уступает пальмы первенства. Это еще раз доказывает, что к введению параллелизма следует подходить с осторожностью, поскольку это не только приводит к усложнению программы, но может приводить и к замедлению ее работы.
