Параллельные вычисления: класс MultiThread
Современные персональные компьютеры стали многопроцессорными (и/или многоядерными), это — факт! Пока еще не вышел .NET Framework 4.0 и C# 4.0, в котором заявлена поддержка многопоточности, и поэтому программисты, как правило, никак не используют параллельность в своих вычислительных программах. Характерно, что активно вычисляющая программа грузит процессор лишь на 50% (на 2-ядерной машине) или на 25% (на 4-ядерной машине), а ведь всегда требуется считать быстрее и грузить процессор по полной.
Например, вы производите какую-либо обработку данных в цикле, выполнение которого занимает на 4-ядерной машине в среднем 20 секунд. Для конечного пользователя иногда это — целая вечность. Согласитесь, пользователь не стал бы жаловаться на тормоза программы, если бы тот же цикл выполнялся за 5 секунд.
Для этого может быть полезен следующий класс:
C#: | Select code |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | using System.Threading; namespace UsingsRU.Utils { /// <summary> /// Класс предназначен для параллельного выполнения вычислений. /// Эффективен в случае многоядерных процессоров (включая процессора с HT). /// <remarks>В вычислительной функции для потоков: при использовании локальных переменных вызывающего метода вместо внутренних полей класса, содержащего этот метод - резкое снижение эффективности.</remarks> /// </summary> public class MultiThread { /// <summary> /// Вычислительная функция для потока threadIndex /// </summary> /// <param name="threadIndex">Порядковый номер потока</param> /// <param name="threadCount">Количество созданных потоков</param> public delegate void ThreadHandler(int threadIndex, int threadCount); static MultiThread() { int maxIOThreads; ThreadPool.GetMinThreads(out minThreads, out maxIOThreads); } /// <summary> /// Создать класс с максимально допустимым количеством потоков MaxThreadsCount /// </summary> /// <param name="MaxThreadsCount">Максимально допустимое количество потоков</param> public MultiThread(int MaxThreadsCount) { threadCount = MaxThreadsCount < minThreads ? MaxThreadsCount : minThreads; } /// <summary> /// Создать класс с максимально допустимым количеством потоков MaxThreadsCount /// </summary> /// <param name="MinThreadsCount">Миниимально допустимое количество потоков</param> /// <param name="MaxThreadsCount">Максимально допустимое количество потоков</param> public MultiThread(int MinThreadsCount, int MaxThreadsCount) { if (MinThreadsCount > MaxThreadsCount) MinThreadsCount = 1; threadCount = MaxThreadsCount < minThreads ? MaxThreadsCount : minThreads; if (MinThreadsCount > threadCount) threadCount = MinThreadsCount; } /// <summary> /// Создать класс с максимально допустимым количеством потоков MaxThreadsCount и дождаться окончания выполнения вычислительной функции для потоков handler /// </summary> /// <param name="MaxThreadsCount">Максимально допустимое количество потоков</param> /// <param name="handler">Вычислительная функция для потоков</param> public MultiThread(int MaxThreadsCount, ThreadHandler handler) : this(MaxThreadsCount) { Run(handler); } /// <summary> /// Создать класс с максимально допустимым количеством потоков MaxThreadsCount и дождаться окончания выполнения вычислительной функции для потоков handler /// </summary> /// <param name="MinThreadsCount">Миниимально допустимое количество потоков</param> /// <param name="MaxThreadsCount">Максимально допустимое количество потоков</param> /// <param name="handler">Вычислительная функция для потоков</param> public MultiThread(int MinThreadsCount, int MaxThreadsCount, ThreadHandler handler) : this(MinThreadsCount, MaxThreadsCount) { Run(handler); } /// <summary> /// Запустить вычисления в функции для потоков handler и дождаться окончания выполнения /// </summary> /// <param name="MaxThreadsCount">Максимально допустимое количество потоков</param> /// <param name="handler">Вычислительная функция для потоков</param> public static void RunThreads(int MaxThreadsCount, ThreadHandler handler) { new MultiThread(MaxThreadsCount, handler); } /// <summary> /// Запустить вычисления в функции для потоков handler и дождаться окончания выполнения /// </summary> /// <param name="handler">Вычислительная функция для потоков</param> public void Run(ThreadHandler handler) { Thread[] t = new Thread[threadCount]; for (int j = 0; j < threadCount; j++) { t[j] = new Thread(delegate(object tn) { handler((int)tn, threadCount); }); t[j].Start(j); } for (int j = 0; j < threadCount; j++) t[j].Join(); } /// <summary> /// Количество созданных потоков /// </summary> public int ThreadCount { get { return threadCount; } } private readonly int threadCount; private readonly static int minThreads; } } |
Данный класс позволяет производить вычисления в несколько потоков. При инициализации, класс сам определит оптимальное количество потоков для той машины, на которой он выполняется.
Для многих, хорошо распараллеливаемых задач, его эффективность близка к 100%.
Рассмотрим на примере:
C#: | Select code |
int total_min = 0; int total_len = 10000000; double[] A = new double[total_len]; MultiThread.RunThreads(total_len, (threadIndex, threadCount) => { int len = total_len / threadCount; int lowBound = total_min + len * threadIndex; int hiBound = (threadIndex == threadCount - 1) ? total_len : lowBound + len; for (int i = lowBound; i < hiBound; i++) A[i] = 0.01 * i * i; });
Данный код будет выполнен примерно в 2 раза быстрее на 2-ядерном компьютере, по сравнению с классическим кодом:
C#: | Select code |
int total_min = 0; int total_len = 10000000; double[] A = new double[total_len]; for (int i = total_min; i < total_len; i++) A[i] = 0.01 * i * i;
Убедитесь сами. Удачи!
Обсуждение на форуме: http://usings.ru/forum/viewtopic.php?t=9
Один ответ Оставить комментарий
Спасибо!!! Используем этот классик повсеместно в проекте. Много раз он нас выручал при решении довольно сложных задач. Респект автору.