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

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

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

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

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


[Назад] [Что осталось за кадром...]

6.7. Диспетчеризация потоков на C#

Это последний и, наверное, самый интересный и наглядный пример программирования на C# с использованием параллельных вычислений. Если на предыдущем шаге была имитация гонок, то этот шаг скорее похож на имитацию процесса регулирования дорожного движения. Диспетчеризация потоков – это управление процессами выделения потокам вычислительных ресурсов, их регламентация и мониторинг за ходом выполнения параллельных вычислений. Как я уже говорил, используя экземпляры классов Thread можно блокировать выбранный поток или вообще завершить его работу. Также, ход выполнения нескольких параллельных потоков можно синхронизировать, заставляя одни из них дожидаться окончания выполнения других с использованием метода Join(). На этом шаге будут рассмотрены способы опосредованной диспетчеризации потоков, которые можно применять не только при работе с экземплярами класса Thread, но также и с потоками из пула потоков.

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

События

Имеются в виду не те события, которые используются для организации обратных вызовов, а “маяки”, которые способны приостанавливать работу потоков и подавать им сигналы для возобновления прерванных вычислений. “Устанавливаются” эти “маяки” непосредственно в код с использованием экземпляров классов AutoResetEvent и ManualResetEvent. Если поток в ходе вычислений встречает вызов метода WaitOne() экземпляра события, то дальнейшие действия могут развиваться по одному из следующих сценариев:

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

  • Поток приостанавливает работу, если событие находится в несигнальном состоянии. Работа потока может быть продолжена, как только событие переходит в сигнальное состояние. Почему "может быть продолжена", а не "будет продолжена" объясняется далее.

Есть два типа события: с ручным и автоматическим режимом перехода в несигнальное состояние, останавливающее потоки. Их разница в следующем: события с “автоматическим перезапуском”, находясь в несигнальном состоянии, “ловят” все потоки, ход выполнения которых натыкается на метод WaitOne(), а после перехода в сигнальное состояние “отпускают” только один из них (любой) и сразу же автоматически снова переходят в несигнальное состояние. События с “ручным перезапуском” также ловят всех, но, будучи переведенными в сигнальное состояние вызовом метода Set(), отпускают тоже всех, пока их принудительно не вернут в несигнальное состояние вызовом метода Reset(). Поэкспериментировать с этими видами событий можно на примере демонстрационной программы. Для запуска примера необходимо нажать кнопку [Старт]. В отличие от предыдущего примера сразу ничего происходить не будет. Если выбран ручной режим, то после нажатия на [Старт] необходимо нажать [Вперед], и потоки “побегут” к финишу, пока не будет нажата кнопка [Стоп]. Чередуя [Стоп]и [Вперед] вы будете останавливать и возобновлять работу всех потоков. Если выбран автоматический режим, то кнопка [Стоп] становится неактивной, а для того чтобы что-то происходило нужно постоянно нажимать на кнопку [Вперед], поскольку каждый раз будет "оживать" лишь один поток и после "короткой перебежки" снова "замирать".

Семафоры

Семафоры – это еще один инструмент управления потоками. Семафоры используют в тех случаях, когда необходимо ограничить число потоков, одновременно выполняющих определенный участок кода. Бывает, что вводят подобные ограничения на доступ к разделяемому ресурсу, который теоретически может обслуживать несколько потоков одновременно, но желательно число таких “клиентов” ограничить. Если вернуться к демонстрационной программе, то там семафоры, установленные на “пути следования” потоков в количестве 2 штуки, призваны “останавливать толпу” и пропускать на определенных отметках лишь ограниченное число потоков за условную единицу времени: по 4 потока на первой отметке и по 2 потока на второй. На картинке выше видно, что первые 4 потока уже пропущены первым семафором, а остальные либо “оформляются на таможне”, либо "ждут своей очереди". Семафор в случае .Net Framework – это экземпляр класса Semaphore, который ограничивает число потоков на участке кода от места вызова метода WaitOne(), до места вызова метода Release(). Максимальное число потоков определяется конструктором класса. Все детали можно изучить, посмотрев на фрагменты исходного кода ниже, но сразу хочу обратить внимание на работу с семафором с использованием оператора try-finally, поскольку необходимо гарантировать освобождение семафора независимо от того, возникнут ли исключительные ситуации в границах его критической секции или нет.

        //Пример 5. Диспетчеризация потоков
        //Список потоков
        private List<Thread> m_Threads;
        //Список элементов управления 'ProgressBar'
        private List<ProgressBar> m_ThreadDispatchingBars;

        //События, управляющие потоками
        //Событие с автоматическим 'повторным' включением
        private AutoResetEvent m_AutoEvent;
        //Событие с ручным 'повторным' включением
        private ManualResetEvent m_ManualEvent;
        //Текущий режим управления потоками с использованием событий
        private bool m_IsAutoEvent = false;
        //Семафоры, управляющие потоками
        //Семафор с пропускной способностью 4, установленный на отметке 2000
        private Semaphore m_Semaphore2000;
        //Семафор с пропускной способностью 2, установленный на отметке 7000
        private Semaphore m_Semaphore7000;

        private void btnStartThreadDispatching_Click(object sender, EventArgs e)
        {
            //Инициализация/завершение работы коллекции потоков
            if (m_Threads != null)
            {
                foreach (Thread _Thread in m_Threads)
                    try
                    {
                        //Если текущий поток активен, то прерываем его и дожидаемся 
                        //окончания его работы
                        if (_Thread.IsAlive) 
                        {
                            _Thread.Abort();
                            _Thread.Join();  
                        }
                    }
                    catch {}

                m_Threads.Clear();  
            }
            else
            {
                m_Threads = new List<Thread>(); 
            }

            //...
            //<исходный код частично отсутствует>
            //...    

            //Инициалиазция событий
            m_AutoEvent = new AutoResetEvent(false);
            m_AutoEvent.Reset();

            m_ManualEvent = new ManualResetEvent(false);
            m_ManualEvent.Reset();

            //Инициализация семафоров величиной пропускной способности и 
            //количеством заранее зарезервированных 'входов'
            m_Semaphore2000 = new Semaphore(4, 4);
            m_Semaphore7000 = new Semaphore(2, 2);

            //Инициализация с использованием псевдо-случайных чисел и старт
            System.Random _rnd;
            _rnd = new System.Random();

            for (int i = 0; i < _ThreadsCount; i++)
            {
                //Расчет величины ускорения в зависимости от выбранного режима управления потоками 
                //при помощи событий
                int _acceleration = 0;

                if (m_IsAutoEvent)
                    _acceleration = 100 + _rnd.Next(100);
                else
                    _acceleration = 10 + _rnd.Next(10);

                //Класс ThreadRaceCompetitor описывает 'участника гонки', 
                //содержит 'технические параметры гонщика'
                //и определяет логику ускорения и переключения передач
                ThreadRaceCompetitor _competitor;
                _competitor = new ThreadRaceCompetitor(i, 100, 1, _acceleration);

                Thread _Thread = new Thread(new ParameterizedThreadStart(ThreadDispatchingProc));
                //Все создаваемые потоки - фоновые и завершаются вместе с основным приложением
                _Thread.IsBackground = true;
                //Старт: ThreadDispatchingProc(_competitor)
                _Thread.Start(_competitor);
                //Добавляем созданный поток в список потоков
                m_Threads.Add(_Thread);  
            }

            //...
            //<исходный код частично отсутствует>
            //...    
        }

        //Прохождение трассы отдельным участником
        private void ThreadDispatchingProc(object state)
        {
            ThreadRaceCompetitor _competitor = (ThreadRaceCompetitor)state;
            ProgressBar _bar = m_ThreadDispatchingBars[_competitor.Index];

            //Основной цикл
            while (_bar.Value < _bar.Maximum)
            {
                //Остановка по требованию события
                if (m_IsAutoEvent)
                    m_AutoEvent.WaitOne();
                else
                    m_ManualEvent.WaitOne();

                //'жмем на газ'...
                _competitor.Accelerate();

                int _newValue = _bar.Value + _competitor.Speed;

                //Обновление элементов управления в потоке пользовательского интерфейса
                //В качестве параметра передаем экземпляр Delegate, параметризованного лямбда - выражением.
                _bar.Invoke(new MethodInvoker(() =>
                {
                    if (_newValue < _bar.Maximum)
                        _bar.Value = _newValue;
                    else
                        _bar.Value = _bar.Maximum;
                }));

                Thread.Sleep(100);

                //Вхождение в зону семафора 1
                if (_bar.Value > 2000 && _competitor.CheckPoint == 0)
                {
                    m_Semaphore2000.WaitOne();

                    try
                    {
                        //Обновление элементов управления в потоке пользовательского интерфейса
                        textBoxDispatchingStatistics.Invoke(new MethodInvoker(() =>
                        {
                            textBoxDispatchingStatistics.Text = textBoxDispatchingStatistics.Text 
                                + "CP-2000: " + (_competitor.Index + 1).ToString() 
                                + " ожидает." + Environment.NewLine;
                        }));

                        //отметка о прохождении семафора 1
                        _competitor.CheckPoint = 1;
                        Thread.Sleep(2000);

                        //Обновление элементов управления в потоке пользовательского интерфейса
                        textBoxDispatchingStatistics.Invoke(new MethodInvoker(() =>
                        {
                            textBoxDispatchingStatistics.Text = textBoxDispatchingStatistics.Text 
                                + "CP-2000: " + (_competitor.Index + 1).ToString() 
                                + " прошел." + Environment.NewLine;
                        }));
                    }
                    finally
                    {   //Гарантированное 'освобождение' семафора
                        m_Semaphore2000.Release(); 
                    }
                }

                //Вхождение в зону семафора 2
                if (_bar.Value > 7000 && _competitor.CheckPoint == 1)
                {
                    m_Semaphore7000.WaitOne();

                    try
                    {
                        //Обновление элементов управления в потоке пользовательского интерфейса
                        textBoxDispatchingStatistics.Invoke(new MethodInvoker(() =>
                        {
                            textBoxDispatchingStatistics.Text = textBoxDispatchingStatistics.Text 
                                + "CP-7000: " + (_competitor.Index + 1).ToString() 
                                + " ожидает." + Environment.NewLine;
                        }));

                        //отметка о прохождении семафора 1
                        _competitor.CheckPoint = 2;
                        Thread.Sleep(2000);

                        //Обновление элементов управления в потоке пользовательского интерфейса
                        textBoxDispatchingStatistics.Invoke(new MethodInvoker(() =>
                        {
                            textBoxDispatchingStatistics.Text = textBoxDispatchingStatistics.Text 
                                + "CP-7000: " + (_competitor.Index + 1).ToString() 
                                + " прошел." + Environment.NewLine;
                        }));
                    }
                    finally
                    {   //Гарантированное 'освобождение' семафора
                        m_Semaphore7000.Release(); 
                    }
                }
            }

            //Отметка о том, что поток 'финишировал'
            textBoxDispatchingStatistics.Invoke(new MethodInvoker(() =>
            {
                textBoxDispatchingStatistics.Text = textBoxDispatchingStatistics.Text 
                    + (_competitor.Index + 1).ToString() 
                    + " финишировал." + Environment.NewLine;
            }));
        }


[Назад]

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

Курс для начинающих программистов на 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. Проект Инициативного Народного Фронта Образования - ИНФО-проект.