Язык программирования C#9 и платформа .NET5
// Вывести числа.Console.Write("Your numbers: ");for (int i = 0; i < 10; i++){Random r = new Random();Thread.Sleep(1000 * r.Next(5));Console.Write("{0}, ", i);}Console.WriteLine();}finally{Monitor.Exit(threadLock);}}Первым делом обратите внимание, что конечным получателем маркера потока, который указывается как аргумент ключевого слова
, является методlock. Весь код внутри областиMonitor. Enter()помещен внутрь блокаlock. Соответствующий блокtryгарантирует освобождение маркера блокировки (посредством методаfinally), даже если возникнут любые исключения времени выполнения. Модифицировав программуMonitor.Exit()с целью прямого применения типаMultiThreadShareData(как только что было показано), вы обнаружите, что вывод идентичен.MonitorС учетом того, что ключевое слово
требует написания меньшего объема кода, чем при явной работе с типомlock, может возникнуть вопрос о преимуществах использования этого типа напрямую. Выражаясь кратко, типSystem.Threading.Monitorобеспечивает большую степень контроля. Применяя типMonitor, можно заставить активный поток ожидать в течение некоторого периода времени (с помощью статического методаMonitor), информировать ожидающие потоки о том, что текущий поток завершен (через статические методыMonitor.Wait()иMonitor.Pulse()), и т.д.Monitor.PulseAll()Как и можно было ожидать, в значительном числе случаев ключевого слова
будет достаточно. Если вас интересуют дополнительные члены классаlock, тогда обращайтесь в документацию по .NET Core.MonitorСинхронизация с использованием типа System.Threading.Interlocked
Не заглядывая в код CIL, обычно нелегко поверить в то, что присваивание и простые арифметические операции не являются атомарными. По указанной причине в пространстве имен
предоставляется тип, который позволяет атомарно оперировать одиночным элементом данных с меньшими накладными расходами, чем типSystem.Threading. В классеMonitorопределены статические члены, часть которых описана в табл. 15.4.InterlockedНесмотря на то что это не сразу видно, процесс атомарного изменения одиночного значения довольно часто применяется в многопоточной среде. Пусть имеется код, который инкрементирует целочисленную переменную-член по имени
. Вместо написания кода синхронизации вроде показанного ниже:intValint intVal = 5;object myLockToken = new();lock(myLockToken){intVal++;}код можно упростить, используя статический метод
.Interlocked.Increment()Методу потребуется передать инкрементируемую переменную по ссылке. Обратите внимание, что метод
не только изменяет значение входного параметра, но также возвращает полученное новое значение:Increment()intVal = Interlocked.Increment(ref intVal);В дополнение к методам
иIncrement()типDecrement()позволяет атомарно присваивать числовые и объектные данные. Например, чтобы присвоить переменной-члену значениеInterlocked, можно обойтись без явного оператора83(или явной логикиlock) и применить методMonitor:Interlock.Exchange()Interlocked.Exchange(ref myInt, 83);Наконец, если необходимо проверить два значения на предмет равенства и изменить элемент сравнения в безопасной к потокам манере, тогда допускается использовать метод
:Interlocked.CompareExchange()public void CompareAndExchange(){// Если значение i равно 83, то изменить его на 99.Interlocked.CompareExchange(ref i, 99, 83);}Программирование с использованием обратных вызовов Timer
Многие приложения нуждаются в вызове специфического метода через регулярные интервалы времени. Например, в приложении может существовать необходимость в отображении текущего времени внутри панели состояния с помощью определенной вспомогательной функции. Или, скажем, нужно, чтобы приложение эпизодически вызывало вспомогательную функцию, выполняющую некритичные фоновые задачи, такие как проверка поступления новых сообщений электронной почты. В ситуациях подобного рода можно применять тип
в сочетании со связанным делегатом по имениSystem.Threading.Timer.TimerCallbackВ целях иллюстрации предположим, что у вас есть проект консольного приложения (
), которое будет выводить текущее время каждую секунду до тех пор, пока пользователь не нажмет клавишу <Enter> для прекращения работы приложения. Первый очевидный шаг — написание метода, который будет вызываться типомTimerApp(не забудьте импортировать в свой файл кода пространство именTimer):System.Threadingusing System;using System.Threading;
