- •Многопоточность
- •Оглавление
- •Задачи многопоточности
- •Методы создания потоков
- •Делегаты
- •Ожидание завершения работы потока
- •Получение результата работы метода, выполнявшегося в отдельном потоке
- •Полезный совет
- •Класс Thread
- •Ожидание завершения потока
- •Управление выполнением потока
- •«Сон» потока
- •Приоритет потоков
- •Фоновые потоки и потоки «переднего плана»
- •Класс ThreadPool
- •Класс Task
- •Создание потоков
- •Ожидание завершения потока
- •Получение результатов выполнения потока
- •Прерывание потока
- •Класс Parallel
- •Метод AsParallel()
- •Синхронизация потоков
- •Оператор lock
- •Класс ReaderWriterLock
- •Класс Mutex
- •Класс WaitHandle
- •Класс AutoResetEvent
- •Класс ManualResetEvent
- •Блокировка потоков
- •Взаимодествие с пользовательским интерфейсом
- •Метод Invoke
- •Использование SynchronizationContext
- •Класс BackgroundWorker
- •Окончание работы и возвращение результата
- •Прогресс выполнения
- •Отмена выполнения метода
- •Объект Dispatcher
- •Класс BackgroundWorker
- •Заключение
Методы создания потоков
Здесь описаны основные методы создания потоков в .NET Framework.
Делегаты
Наверное наиболее простым методом выполнения какого-либо метода в отдельном потоке является использование делегата (проект Delegates). Пусть у нас есть метод, выполняющий какую-либо длительную задачу (класс Exercise1):
private void CheckPrimeNumber(long checkNumber){…}
Для того, чтобы выполнить его в отдельном потоке, необходимо создать делегат, соответствующий сигнатуре данного метода:
private delegate void CheckPrimeNumberDelegate(long checkNumber);
Затем создайте объект делегата, передав его конструктору ваш метод, который вы хотите выполнить в отдельном потоке:
CheckPrimeNumberDelegate dlgt = new CheckPrimeNumberDelegate(CheckPrimeNumber);
Запуск выполнения метода в новом потоке осуществляется вызовом метода BeginInvoke делегата.
dlgt.BeginInvoke(1000000021, null, null);
Этому методу передаются те же параметры, что передавались бы методу CheckPrimeNumber, а так же некоторые другие. Вот и все. Ваш метод будет выполнен в отдельном потоке, не останавливая выполнение основного потока.
Ожидание завершения работы потока
Рассмотренный нами случай был наиболее простым. Однако предположим, что наш метод, который должен выполняться в отдельном потоке, возвращает некоторый результат:
private static long GetDivider(long checkNumber){…}
Очевидно, нам необходимо знать две вещи:
Когда поток завершил работу?
Как получить результат, возвращенный функцией, выполнявшейся в этом потоке?
Рассмотрим решение первой задачи. Как и в предыдущем случае необходимо создать делегат, соответствующий нашему методу, создать его экземпляр и вызвать метод BeginInvoke (класс Exercise2).
private delegate long GetDividerDelegate(long checkNumber);
GetDividerDelegate dlgt = new GetDividerDelegate(GetDivider);
Однако теперь нас интересует объект, возвращаемый методом BeginInvoke:
IAsyncResult res = dlgt.BeginInvoke(1000000021, null, null);
Первый способ дождаться завершения созданного нами потока – проверять свойство IsCompleted объекта IAsyncResult:
while (!res.IsCompleted)
{
continue;
}
Внутри данного цикла вы можете производить какую-либо работу в основном потоке.
Второй подход заключается в использовании объекта, возвращаемого свойством AsyncWaitHandle объекта IAsyncResult (класс Exercise3):
res.AsyncWaitHandle.WaitOne();
Более подробно об объектах WaitHandle мы будет говорить в разделе, посвященном синхронизации потоков. Сейчас же важно то, что вызов метода WaitOne этого объекта позволяет нам дождаться завершения нашего потока. Однако знайте, что вызов этого метода блокирует вызвавший его поток до тех пор, пока не будет завершен ваш поток.
В связи с этим наверно самым удобным способом получения уведомления о том, что ваш поток завершился, является делегат обратного вызова (класс Exercise4). Этот делегат передается одним из параметров метода BeginInvoke:
dlgt.BeginInvoke(1000000021, new AsyncCallback(GetDividerFinished), null);
Сигнатура (возвращающие значения и типы параметров) этого метода:
private static void GetDividerFinished(IAsyncResult res){…}
Как видно, ему передается такой же объект IAsyncResult, что возвращался при вызове метода BeginInvoke. При использовании такого подхода основной поток не будет заблокирован, а в момент завершения вашего потока будет вызван метод GetDividerFinished.
