не хватает одного параметра: 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); } }
Взглянем теперь на результаты численных экспериментов и посмотрим, что дает введение потоков в алгоритм быстрой сортировки:
Как видите, все три метода легко справляются с сортировкой достаточно больших массивов. Время сортировки практически сравнимо с величиной ошибки измерения времени. Введение потоков не дает никаких преимуществ. Классический последовательный алгоритм не уступает пальмы первенства. Это еще раз доказывает, что к введению параллелизма следует подходить с осторожностью, поскольку это не только приводит к усложнению программы, но может приводить и к замедлению ее работы.