Г. Шилдт - С#4.0 Полное руководство (1160795), страница 183
Текст из файла (страница 183)
Затем вызывается метод Рог (), которому в качестве "тела" цикла передается метод мутгапз Согш () . Этот метод состоит из ряда операторов, выполнлющих произвольные преобразования в массиве бага. Его назначение — сымитировать конкретную операцию. Как будет подробнее пояснено несколько ниже, выполняемая операция должна быть нетривиальной, чтобы параллелизм данных принес какой-то положительный эффект. В противном случае последовательное выполнение цикла может завершиться быстрее. 910 Часть ]]. Библиотека С№ // Применить метод Рага11е1.Гог() для организации параллельно // выполняемого цикла обработки данных.
цяьпд Зуясевг цяьпс Зуягев. Тьгеабьпч. Таякя; с1аяя Оеворага11е1Рог ( ягаг1с Епс[] бага; // Метод, служащий в качестве тела параллельно выполняемого цикла. // Операторы этого цикла просто расходуют время ПП для целей демонстрации. ясасьс чоьб мутгвпяеогв(гпс 1) ( бага[1] = бага[1] / 10," ЕЕ(баса[1] < 10000) бага[1) = 0; ЕЕ(бага[1] > 10000 я баса[1) < 20000) баса[1] = 100; ЕЕ(баса(1] > 20000 ь баса[1] < 30000) баса[1] = 200; ЕЕ шага[1] > 30000) баса[1] = 3001 ) ягагьс чо1д МаЕп () ( Сопяо1е. Игьгеььпе (" Основной поток запущен.
"); баса = пен Епг(100000000); Инициализировать данные в обычном цикле Еог. еог(1пг >=01 1 < баса.ьепчсю 1++) баса[я] = 11 Распараллелить цикл методом Рог(). Рага11е1.Рог(0, баса.ьепчс)>, мутгапяЕошв) Сопяо1е.иг1геььпе("Основной поток завершен."); Эта программа состоит из двух циклов. В первом, стандартном, цикле Еог инициализируется массив баса. А во втором цикле, выполняемом параллельно методом рог (), над каждым элементом массива бага производится преобразование. Как упоминалось выше, это преобразование носит произвольный характер и выбрано лишь для целей демонстрации. Метод Рот () автоматически разбивает вызовы метода МуТгапя Еогв () на части для параллельной обработки отдельных порций данных, хранящихся в массиве.
Следовательно, если запустить данную программу на компьютере с двумя доступными процессорами или больше, то цикл преобразования данных в массиве может быть выполнен методом рог () параллельно. Следует, однако, иметь в виду, что далеко не все циклы могут выполнятьсд эффективно, когда они распараллеливаются. Как правило, мелкие циклы, а также циклы, состоящие из очень простых операций, выполндютсв быстрее последовательным способом, чем параллельным. Именно поэтому цикл Еог инициализации массива данных не распараллеливается методом рог () в рассматриваемой здесь программе. распараллеливание мелких и очень простых циклов может оказаться неэффективным потому, что время, требующееся для организации параллельных задач, а также время, расходуемое на переключение контекста, превышает время, экономящееся благодари параллелизму.
В подтверждение этого факта в приведеннрм ниже примере программы Гвава 24. Многопоточное программирование. Часть вторая: бибяиотекаТРЬ 911 саздакпся последовательный и параллельный варианты цикла гог, а для сравнения на экран выводится время выполнения каждого из них. // Продемонстрировать отличия во времени последовательного // и параллельного выполнения цикла гог. ця1по Яувсеш) цятпч Яуягеш.тьгеаб1пд.таяьяг цятпч Яуягеш.01ачпоЯС1сяг с1аяв Оешорага11е1Рог ( вгаг1с гпг[] бага; // Метод, служащий в качестве тела параллельно выполняемого цикла.
// Операторы этого цикла просто расходуют время ЦП для целей демонстрации. ягаг1с но1б МУТгапябогш(гпС г) ( баСа[1] = баСа[г] / 10; 1Т(дага[1] < 1000) бага[С] = 0; гг(баса(г] > 1000 ь бага(1] < 2000) баса(1] = 100; ге(баса[>] > 2000 я баса[г] < 3000) баса[1) = 200) 1Р(дага[1] > 3000) дага[>] = 300; ] ягаггс тодд Маги() ( Сопяо1е.нг1СеЬгпе("Основной поток запущен."); // Сгеаге экземпляр объекта типа ЯСорнассп // для хранения времени выполнения цикла.
Ягорнагсь ям = пен Ягорнагсп(); дага = пен 1пС[100000000]) // Инициализировать данные. ян.ЯСагг(Г; // Параллельньй вариант инициализации массива в цикле. Рага11е1.Рог(0, дага.ьепОСХ, (г) => баСа[г) ян.Ягор(); Сопво1е.нггсеЬгпе("Параллельно выполняемый цикл инициализации: "(0) секунд", ян.Е1аряеб.ТоСа15есопбя); ям.невес()г ян.ЯСагС(); // последовательньят вариант инициализации массива в цикле. гог(1пг г=Ог г < бага.Ьепчг)п 1>+) баСа[г] = ).; ян.Ягор()) Сопяо1е.нгггевьпе("Последовательно выполняеьнй цикл инициализации: "(0] секунд", ян.Е1арвеб.Тога15есопбя); Сопяо1е.иггкевппе(); 912 Часть П. Библиотека СФ Выполнить преобразования.
вм.зсагс()) Параллельный вариант преобразования данных в цикле. Рага11е1.гог(0, г)аса.сепчси, Мутгапзтогв)) вм.зсор(); Сопво1е.шг1севьпе("Параллельно выполняемый цикл преобразования: "(О) секунд", вм.Е1арзег(.тоса1зесопба)) ам.ревет (); вм.згагг(); Последовательный вариант преобразования данных в цикле. тот(ьпг г=с) ь < бага.ьепчгю г++) мутгапагогв(г)г вм. Бгор (); Сопво1е.шгьсеглпе("Последовательно выполняемый цикл преобразования: "(О) секунд", вн.Е1араеб.тога18есопбв); Оопво1е.нгтгеьгпе("Основной поток завершен."): ) При выполнении этой программы на двухьядерном компьютере получается следующий резуль тат.
Основной поток запущен. Параллельно выполняемый цикл инициализации: 1.0537757 секунд Последовательно выполняемый цикл инициализации: 0.3457828 секунд Параллельно выполняемый цикл преобразования: 4.2248875 секунд Последовательно выполняемый цикл преобразования: 5.3849959 секунд Основной поток завершен. Прежде всего, обратите внимание на то, что параллельный вариант цикла инициализации массива данных выполняется приблизительно в три раза медленнее, чем последовательный. Дело в том, что в данном случае на операцию присваивания расходуется так мало времени, что издержки на дополнительно организуемое распараллеливание превышают экономию, которую оно дает. Обратите далее внимание на то, что параллельный вариант цикла преобразования данных выполняется быстрее, чем последовательный.
В данном случае экономия от распараллеливания с лихвой возмещает издержки на его дополнительную организацию. ПРИМЕЧАНИЕ Как правило, а отношении преимуществ, которые дает распараллеливание различных видов циклов, следует руководствоваться текущими рекомендациями корпорации М(сговой. Кроме того, необходимо убедиться а том, что распараллеливание цикла действительно приводит к повышению производительности, прежде чем использовать такой цикл в окончательно выпускаемом прикладном коде. Глава 24.
Многопоточное программирование. Часть вторагп библиотека ТРЬ 913 Что касается приведенной выше программы, то необходимо упомянуть о двух других ее особенностях. Во-первых, обратите внимание на то, что в параллельно выполняемом цикле для инициализации данных применяется лямбда-выражение, как показано ниже. Рага11е1.гог(0, Яаьа.ьепдьп, (1) => йаьа(1] = 1 Здесь "тело" цикла указывается в лямбда-выражении. (Напомним, что в лямбдавыражении создается анонимный метод.) Следовательно, для параллельного выполнения методом Рог () совсем не обязательно указывать именованный метод. И во-вторых, обратите внимание на применение класса Ясориассь для вычисления времени выполнения цикла.
Этот класс находится в пространстве имен Я уз Сепг. ()1адпояс1ся. Для того чтобы воспользоваться им, достаточно создать экземпляр его объекта, а затем вызвать метод Ясагс (), начинающий отчет времени, и далее — метод Ягор ( ), завершающий отсчет времени. А с помощью метода Ве ее С ( ) отсчет времени сбрасывается в исходное состояние. Продолжительность выполнения можно получить различными способами. В рассматриваемой здесь программе для этой цели использовано свойство В1аряес(, возвращающее обьекг ~игга Т1гиеярап. С помощью этого объекта и свойства тога1Яесопс(я время отображается в секундах, включая и доли секунды. Как показывает пример рассматриваемой здесь программы, класс ЯСорнагсЬ оказывается весьма полезным при разработке параллельно исполняемого кода.
Как упоминалось выше, метод Рог () возвращает экземпляр объекта типа Ра га 11е1ьоорре я и 1 с. Это структура, в которой определяются два следующих свойства. риЫ>с Ьоо1 1яоопр1егеи ( деш риь11с ни11аь1е<1опд> ьочея<В<еах1сегастоп ( дею Свойство Тясовгр1есес( будет иметь логическое значение сгие, если выполнены все шаги цикла. Иными словами, при нормальном завершении цикла это свойство будет содержать логическое значение сгие. Если же выполнение цикла прервется раньше времени, то данное свойство будет содержать логическое значение Та1яе.
Свойство ьонгеясвгеахтсегас1оп будет содержать наименьшее значение переменной управления циклом, если цикл прервется раньше времени вызовом метода Рага11е1ЬоорЯСаге.вгеак(). Для доступа к объекту типа Рага11е1ЬоорЯСаее следует использовать форму метода Рог ( ), делегат которого принимает в качестве второго параметра текущее состояние цикла. Ниже эта форма метода Рог () приведена в простейшем виде. риьттс яьаг(с Рага11е1Ьоорнеяи11 Рог(1пг Ггоятпс1иятне, гпг Сояхс1ия1не, Асп1оп<тпи, Рага11е1Ьооряиаие> Ьо<)у) В данной форме делегат Асетоп, описывающий тело цикла, определяется следующим образом. риЬ1гс с(е1едапе носа Аситоп<(п Т1, сп Т2>(Т агд1, Т2 агд2) Для метода Рог () обобщенный параметр т1 должен быть типа тпс, а обобщенный параметр т2 — типа Рага11е1ьоорвсасе. Всякий раз, когда делегат Асстоп вызывается, текущее состояние цикла передаетсл в качестве аргумента агд2.
Длл преждевременного завершения цикла следует воспользоваться методом В ге а]< ( ), вызываемым длл экземпляра объекта типа Рага11е1ЬоорЯСаее внутри тела цикла, определяемого параметром Ьос(у. Метод вгеа)с () объявляется следующим образом. риь11с натз Вгеак() 914 Часть (1. Библиотека С(] Вызов метода Вгеак () формирует запрос на как можно более раннее прекращение параллельно выполняемого цикла, что может произойти через несколько шагов цикла после вызова метода Вгеак П. Но все шаги цикла до вызова метода Вгеак () все же выполняются. Следует, также иметь в виду, что отдельные части цикла могут и не выполнлтьсл параллельно. Так, если выполнено 10 шагов цикла, то это еще не означает, что все эти 10 шагов представляют 10 первых значений переменной управления циклом.