Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Троелсен Эндрю

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 279 280 281 282 283 284 285 286 287 ... 642

AutoResetEvent _waitHandle = new AutoResetEvent(false);

Console.WriteLine("***** Adding with Thread objects *****");

Console.WriteLine("ID of thread in Main(): {0}",

  Thread.CurrentThread.ManagedThreadId);

AddParams ap = new AddParams(10, 10);

Thread t = new Thread(new ParameterizedThreadStart(Add));

t.Start(ap);

// Ожидать, пока не поступит уведомление!

_waitHandle.WaitOne();

Console.WriteLine("Other thread is done!");

Console.ReadLine();

...

Когда другой поток завершит свою работу, он вызовет метод Set() на том же самом экземпляре типа AutoResetEvent:

void Add(object data)

{

  if (data is AddParams ap)

  {

    Console.WriteLine("ID of thread in Add(): {0}",

        Thread.CurrentThread.ManagedThreadId);

    Console.WriteLine("{0} + {1} is {2}",

        ap.a, ap.b, ap.a + ap.b);

<b>    // Сообщить другому потоку о том, что работа завершена.</b>

    _waitHandle.Set();

  }

}

Потоки переднего плана и фоновые потоки

Теперь, когда вы знаете, как программно создавать новые потоки выполнения с применением типов из пространства имен System.Threading, давайте формализуем разницу между потоками переднего плана и фоновыми потоками.

Потоки переднего плана имеют возможность предохранять текущее приложение от завершения. Среда .NET Core Runtime не будет прекращать работу приложения (скажем, выгружая обслуживающий домен приложения) до тех пор, пока не будут завершены все потоки переднего плана.

Фоновые потоки (иногда называемые потоками-демонами) воспринимаются средой .NET Core Runtime как расширяемые пути выполнения, которые в любой момент времени могут быть проигнорированы (даже если они заняты выполнением некоторой части работы). Таким образом, если при выгрузке домена приложения все потоки переднего плана завершены, то все фоновые потоки автоматически уничтожаются.

Важно отметить, что потоки переднего плана и фоновые потоки — не синонимы первичных и рабочих потоков. По умолчанию каждый поток, создаваемый посредством метода Thread.Start(), автоматически становится потоком переднего плана. В итоге домен приложения не выгрузится до тех пор, пока все потоки выполнения не завершат свои единицы работы. В большинстве случаев именно такое поведение и требуется.

Ради доказательства сделанных утверждений предположим, что метод Printer.PrintNumbers() необходимо вызвать во вторичном потоке, который должен вести себя как фоновый. Это означает, что метод, указываемый типом Thread (через делегат ThreadStart или ParametrizedThreadStart), должен обладать возможностью безопасного останова, как только все потоки переднего плана закончат свою работу. Конфигурирование такого потока сводится просто к установке свойства IsBackground в true:

(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})

Console.WriteLine(&quot;***** Background Threads *****n&quot;);

Printer p = new Printer();

Thread bgroundThread =

  new Thread(new ThreadStart(p.PrintNumbers));

// Теперь это фоновый поток.

bgroundThread.IsBackground = true;

bgroundThread.Start();

Обратите внимание, что в приведенном выше коде не делается вызов Console.ReadLine(), чтобы заставить окно консоли оставаться видимым, пока не будет нажата клавиша <Enter>. Таким образом, после запуска приложение немедленно прекращается, потому что объект Thread сконфигурирован как фоновый поток. С учетом того, что точка входа приложения (приведенные здесь операторы верхнего уровня или метод Main()) инициирует создание первичного потока переднего плана, как только логика в точке входа завершится, домен приложения будет выгружен, прежде чем вторичный поток сможет закончить свою работу.

Однако если закомментировать строку, которая устанавливает свойство IsBackground в true, то обнаружится, что на консоль выводятся все числа, поскольку все потоки переднего плана должны завершить свою работу перед тем, как домен приложения будет выгружен из обслуживающего процесса.

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

Проблема параллелизма

При построении многопоточных приложений необходимо гарантировать, что любой фрагмент разделяемых данных защищен от возможности изменения со стороны сразу нескольких потоков. Поскольку все потоки в домене приложения имеют параллельный доступ к разделяемым данным приложения, вообразите, что может произойти, если множество потоков одновременно обратятся к одному и тому же элементу данных. Так как планировщик потоков случайным образом приостанавливает их работу, что если поток А будет вытеснен до завершения своей работы? Тогда поток В прочитает нестабильные данные.

Чтобы проиллюстрировать проблему, связанную с параллелизмом, давайте создадим еще один проект консольного приложения под названием MultiThreadedPrinting. В приложении снова будет использоваться построенный ранее класс Printer, но на этот раз метод PrintNumbers() приостановит текущий поток на сгенерированный случайным образом период времени.

1 ... 279 280 281 282 283 284 285 286 287 ... 642
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Троелсен Эндрю бесплатно.
Похожие на Язык программирования C#9 и платформа .NET5 - Троелсен Эндрю книги

Оставить комментарий