Главная · Карта сайта · Поиск · Статьи · Компьютерные курсы · Обучающие программы · Открытые проекты · Веб-программирование · Создание интернет-сайта · Полезные ссылки · Глоссарий · Контакты · Декабрь 06 2016 22:49:01
Последнее опубликованное

Что такое Model-View-Controller
Pattern Model-View-Controller

Как создать свой веб-сайт
Как создать свой сайт в интернете

Разное
Статистика

Программирование на C#. Параллельные вычисления. Пул потоков


[Назад] [Следующая страница]

6.4. Конструкция Parallel.Invoke и пул потоков

Конструкция Parallel.Invoke и пул потоков, по сути, предоставляют одни и те же возможности – позволяют выполнять функциональность, сгруппированную в отдельных методах, параллельно. И то и другое достаточно легко использовать. Пул потоков управляется системой, которая сама занимается их созданием и диспетчеризацией. Если у вас нет необходимости самостоятельно управлять потоками параллельных вычислений, а требуется в фоновом режиме выполнить ряд задач, то рекомендуется воспользоваться именно пулом потоков, а не тратить ресурсы на создание своего, выделенного только для ваших целей потока. При использовании пула потоков для ваших вычислений будет предоставлен поток из числа уже имеющихся в пуле, а время на их создание и инициализацию, в том числе на выделение системных ресурсов, потрачено не будет. Все потоки в пуле потоков являются фоновыми, а значит, принудительно завершаются системой после завершения последнего основного потока в процессе вашего приложения. Более подробно узнать про тонкости инициализации потоков можно в msdn, а на данный момент того, что было сказано пока достаточно.

Использовать пул потоков не получится, когда вам требуется, например, создать основной поток, определить потоку более высокий или более низкий по отношению к значению по умолчанию приоритет, в соответствии с которым система будет выделять ему процессорное время. Также не получится самостоятельно приостанавливать и возобновлять работу потока. Не следует использовать пул потоков, когда ваши вычисления “слишком тяжелые”, что вызовет “захват и удержание” на долгое время ценных вычислительных ресурсов. Создание выделенных потоков будет рассмотрено в следующих примерах.

Вернемся к самому примеру. Пользователю предлагается вводить произвольный текст. Обработчик события изменения текста будет запускать таймер, время срабатывания которого также можно определить посредством пользовательского интерфейса. Таймер через заданное время запустит с использованием конструкции Parallel.Invoke или пула потоков (определяется галочкой “Использовать пул потоков”) в фоновом режиме две процедуры обработки введенного текста: "замена цифр соответствующими словами" и "перевод текста в верхний регистр". Обе процедуры работают в независимых потоках и независимо друг от друга меняют содержимое элемента управления текстом. Ниже приведен пример работы программы: два ”скриншота”, сделанные с интервалом в одну секунду после ввода текста.

123456789-qwerty
asbcd-12-34-12-YTREWQ-TT-12

Пример работы обучающей программы (1) Пример работы обучающей программы (2)
Пример работы программы с конструкцией Parallel.Invoke.

На первом рисунке видно, что результат перевода цифр в соответствующие им слова еще не переведен в верхний регистр. На втором рисунке уже виден окончательный результат фоновой обработки текста.

Особо комментировать фрагменты кода опять же не буду, но по поводу работы таймера стоит отметить, что процедура TimerHandler также выполняется в одном из потоков пула, выбор которого осуществляет система. Это важно, поскольку обновление элементов управления возможно только из основного потока, и для этой цели следует использовать свойство InvokeRequired и метод Invoke обновляемого элемента управления (справедливо для Windows Forms).

        //Пример 2. Parallel.Invoke и пул потоков
        //Таймер
        private System.Threading.Timer m_Timer;
        //Вводимый текст
        private string m_Text;
        //Объект - блокировка для разграничения доступа к тексту
        private object m_TextLock = new object(); 

        //Обработка события изменения вводомого текста
        private void textBoxParallelInvoke_TextChanged(object sender, EventArgs e)
        {
            //Период срабатывания таймера
            int _TimerDelay = System.Convert.ToInt32(numericUpDownParallelInvokeTimer.Value) * 100;
            //Обрабатываемый текст
            m_Text = textBoxParallelInvoke.Text;  
            //Запуск/перезапуск вызова метода TimeHandler через _TimerDelay миллисекунд
            if (m_Timer == null)
                m_Timer = new System.Threading.Timer(TimerHandler, "ParallelInvoke", _TimerDelay , 0); 
            else
                m_Timer.Change(_TimerDelay , 0);  
        }

        //При закрытии формы необходимо 'обнулить' таймер
        private void Form_Closing(object sender, EventArgs args)
        {
            if (m_Timer == null)
                return;

            m_Timer.Dispose();
            m_Timer = null;
        }

В зависимости от значения checkBoxThreadPool фоновая обработка запускается посредством прямого использования пула потоков или с помощью конструкции Parallel.Invoke (см. код TimerHandler).

Если свойство InvokeRequired возвращает true, то это означает, что текущий поток не является основным. Для выполнения программного кода обработки элемента пользовательского интерфейса в основном потоке необходимо вызвать метод Invoke и передать ему ссылку на обработчик (см. код UpdateTextHandler).

        //Обработка срабатывания таймера
        private void TimerHandler(object sender)
        {
            if (checkBoxThreadPool.Checked)
            {//Запуск параллельных вычислений посредством пула потоков
                ThreadPool.QueueUserWorkItem((object state) => { DigitsToString(); });
                ThreadPool.QueueUserWorkItem((object state) => { TextToUpperCase(); });
            }
            else//Запуск параллельных вычислений посредством конструкции Parallel.Invoke
                Parallel.Invoke(DigitsToString, TextToUpperCase);
        }
        //Обработчик события изменения m_Text
        //Гарантирует работу с textBoxParallelInvoke ТОЛЬКО в потоке пользовательского интерфейса
        private void UpdateTextHandler(object sender, EventArgs args)
        {
            try
            {
                if (this.InvokeRequired) 
                {//Текущий поток - НЕ поток пользовательского интерфейса, поэтому
                 //осуществляем рекурсивный вызов UpdateTextHandler для выполнения
                 //обработки в потоке пользовательского интерфейса
                    this.Invoke(new EventHandler(UpdateTextHandler));
                    return;
                }
            }
            catch { return; }

            //Текущий поток - поток пользовательского интерфейса.
            textBoxParallelInvoke.Text = m_Text;
            textBoxParallelInvoke.SelectionStart = textBoxParallelInvoke.Text.Length;   
        }

Ниже приведены сами процедуры фоновой обработки. Ничего сложного в них нет, но ради разнообразия и с целью показать, для чего разработчиками языка C# была введена уже знакомая вам конструкция потокобезопасного доступа к разделяемому ресурсу lock {}, в этом примере вместо нее я использовал методы Monitor.Enter и Monitor.Exit совместно с конструкцией try-finally. Именно эту громоздкую конструкцию и заменяет элегантная lock {}, пример использования которой был приведен в предыдущем примере.

        //Обработка изменения m_Text
        private void UpdateText()
        {
            try
            {
                Invoke(new EventHandler(UpdateTextHandler));
            }
            catch { return; }
        }

        //Преобразование цифр в текстовое представление
        private void DigitsToString()
        {
            for (int i = 0; i < 10; i++)
            {
                //Потокобезопасная блокировка (аналог lock() {})
                Monitor.Enter(m_TextLock);

                try
                {
                    switch (i)
                    {
                        case 0: { m_Text = m_Text.Replace("0", "[zero]"); break; }
                        case 1: { m_Text = m_Text.Replace("1", "[one]"); break; }
                        case 2: { m_Text = m_Text.Replace("2", "[two]"); break; }
                        case 3: { m_Text = m_Text.Replace("3", "[three]"); break; }
                        case 4: { m_Text = m_Text.Replace("4", "[four]"); break; }
                        case 5: { m_Text = m_Text.Replace("5", "[five]"); break; }
                        case 6: { m_Text = m_Text.Replace("6", "[six]"); break; }
                        case 7: { m_Text = m_Text.Replace("7", "[seven]"); break; }
                        case 8: { m_Text = m_Text.Replace("8", "[eight]"); break; }
                        case 9: { m_Text = m_Text.Replace("9", "[nine]"); break; }
                    }
                }
                finally
                {
                    //Гарантированный выход из блокировки
                    Monitor.Exit(m_TextLock);
                }

                UpdateText();
                //Пауза, необходимая для обновления пользовательского интерфейса.
                Thread.Sleep(10);
            }
        }

        //Перевод текста в 'верхний' регистр
        private void TextToUpperCase()
        {
            //Потокобезопасная блокировка (аналог lock() {})
            Monitor.Enter(m_TextLock);

            try
            {
                m_Text = m_Text.ToUpper();
            }
            finally
            {
                //Гарантированный выход из блокировки
                Monitor.Exit(m_TextLock);
            }

            UpdateText();
            //Пауза, необходимая для обновления пользовательского интерфейса.
            Thread.Sleep(100);
        }


[Назад] [Следующая страница]

Компьютерные курсы и курсы программирования
Основы программирования

Курс для начинающих программистов на C# и VB.NET.

SQL 25™

Построение SQL запросов и работа с базой данных.

C# Quick Guide™

Программирование на C#. Краткое руководство.

RegEx

Применение регулярных выражений.

Plug-in архитектура

Примеры программной Plug-in архитектуры.

XML и его расширения

Язык разметки XML и его расширения с примерами.

HTML и разметка гипертекста

Языки HTML, XHTML и CSS с примерами разметки.

Основы веб-дизайна

Основы веб-дизайна: решения типовых задач верстки.

Программирование на PHP

Руководство по программированию на PHP для начинающих.

Справочные материалы

Шаблоны проектирования
Каталог шаблонов проектирования программных компонентов.

Рефакторинг кода
Каталог приемов рефакторинга программного кода.

Гость
Имя

Пароль



Забыли пароль?
Запросите новый здесь
.
Coding Craft. Все права защищены © 2011. Проект Инициативного Народного Фронта Образования - ИНФО-проект.