Учебно-методический комплекс общее количество часов 58 ч. Лекции 28 ч
Download 2.46 Mb.
|
Язык программирования C#
15.4. Синхронизация потоков
Создать многопоточное приложение, как вы видите, не сложно. Сложность заключается в синхронизации работы нескольких потоков. Использование поток наиболее эффективно, когда они не разделяют общих ресурсов. Часто в таких случаях не требуется даже синхронизация.В реальности же часто работа одного потока часто зависит от другого, например поток, получающий данные от сервера, не может начать работу до тех пор, пока поток, выполняющий соединение с этим сервером, не выполнит свою. Часто также потоки используют общий файл или общую область памяти для работы с данными. Во всех подобных случая, чтобы не допустить сбоя программы, необходима синхронизация. Для синхронизации в C# используются множество средств, здесь мы рассмотрим только самое простые – это ключевое слов lock и класс System.Threading.Monitor.. Все команды для синхронизации должны происходит внутри lock блоков. Object thisLock = new Object(); lock (thisLock) { // конфликтующий код } Лист. 5.15 Внутри этих блоков допустимо использование функций класса System.Threading.Monitor, используемых для синхронизации доступа потока к общим ресурсам. Самые употребительные из них: Wait(), Pulse() и PulseAll(). Каждая из перегрузок этих функций получает в качестве основного параметра ссылку на объект, доступ к которому следует разделить. Приведу код программы, использующий синхронизацию для доступа к массиву целых числел, который одновременно используется и для чтения и для записи. using System; using System.Threading; namespace Sync { class InputOutput { // размер буфера static int BufferLength = 100; // буфер int[] Buffer; // количество запоненных в буфере мест int InputCounter = 0; public InputOutput() { // создаем синхронизируемый объект Buffer Buffer = new int[BufferLength]; // заполняем Buffer пробелами for (int i = 0; i < Buffer.Length; i++) Buffer[i] = 0; } public void Input() { lock (Buffer) { for (int i = 0; i < Buffer.Length; i++) { InputCounter++; if (InputCounter % 5 == 0) Thread.Sleep(0); Buffer[i] = i + 1; } } } public void Output() { for (int i = 0; i < Buffer.Length; i++) { if (i >= InputCounter) Thread.Sleep(0); Console.WriteLine(Buffer[i]); } } } class Program { static void Main(string[] args) { InputOutput SyncIO = new InputOutput(); // создаем и запускаем поток ввода new Thread(SyncIO.Input).Start(); // создаем и запускаем пток вывода new Thread(SyncIO.Output).Start(); } } } Лист. 5.16 В качестве примера сущности, нуждающейся в синхронизации, здесь выступает класс InputOutput, а точнее его свойство массив Buffer. Этот массив с одной стороны заполняется методом Input(), с другой стороны, введенные в него значения, выводятся методом Output(). В методе Main() поток ввода и поток вывода запускаются одновременно и, следовательно, их необходимо синхронизировать. Синхронизация здесь осуществляется темь, что во время записи в массив данных он запирается конструкцией lock. lock (Buffer) { for (int i = 0; i < Buffer.Length; i++) { InputCounter++; if (InputCounter % 5 == 0) Thread.Sleep(0); Buffer[i] = i + 1; } } Лист. 5.17 С другой стороны выводящий поток может попытать считать еще не заполненные ячейки массива, тогда он должен приостановить выполнение и дать массиву заполниться дальше. if (i >= InputCounter) Thread.Sleep(0); Лист. 5.18 Вывод программы: 1
Вывод. 5.3 Download 2.46 Mb. Do'stlaringiz bilan baham: |
ma'muriyatiga murojaat qiling