...

Многопоточная программа c что это

Потоки в C# .NET первые шаги

Уважаемые читатели, в этой статье я хочу рассказать о таком важном средстве многозадачного программирования среды .NET, как многопоточность. Данная статья содержит начальные сведения, и предназначена для быстрого освоения азов многопоточности на языке C#. Однако не буду разглагольствовать о преимуществах параллельного выполнения задач, и перейду к примеру кода.

 using System; using System.Threading; //Именно это пространство имен поддерживает многопоточность namespace ConsoleApplication1 < class Program < static void Main(string[] args) < Thread myThread = new Thread(func); //Создаем новый объект потока (Thread) myThread.Start(); //запускаем поток for (int i = 0; i < 10; i++ ) < Console.WriteLine("Поток 1 выводит " + i); Thread.Sleep(0); >Console.Read(); //Приостановим основной поток > //Функция запускаемая из другого потока static void func()

На что стоит обратить внимание в этой программе. В первую очередь это пространство имен System.Threading. Это пространство имен содержит в себе классы поддерживающие многопоточное программирование. И именно там содержится класс Thread, который мы используем далее в коде.
Далее мы создаем объект потока.

Thread myThread = new Thread(func);

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

Итак, главный и второстепенный поток параллельно выполняют почти идентичный код они считают до 9 (как повелось, с нуля), перед этим назвав собственный номер. Обратите внимание на статический метод Thread.Sleep(0). Он приостанавливает поток вызвавший его на количество миллисекунд указанных в параметре. Но в данном случае ему передан параметр 0. Это означает что поток должен приостановиться для того чтобы дать возможность выполнения другому потоку.

И для того чтобы консоль не закрылась раньше времени, мы будем ожидать ввода с клавиатуры (Console.Read).В результате ваша программа выведет в консоль примерно следующее:
Поток 1 выводит 0
Поток 2 выводит 0
Поток 1 выводит 1
Поток 1 выводит 2
Поток 1 выводит 3
Поток 2 выводит 1
и т.д.

Хотя результат вывода каждый раз будет разным. Это зависит от множества факторов.Стоит принять во внимание, что потоки бывают двух видов: приоритетные и фоновые. Фоновые потоки автоматически завершаются при завершении приоритетных. По умолчанию в приоритетном потоке запускается функция Main а остальные потоки создаются фоновыми. Именно поэтому мы должны следить за тем, чтобы главный поток не завершился до окончания производных. Для того чтобы не допустить этого мы можем использовать поле IsAlive, которое возвращает true если поток активен. Либо метод Join(), который заставляет поток в котором он вызван ожидать завершения работы потока которому он принадлежит.

Однако мы можем и сами менять виды потоков. Свойство потока IsBackground определяет является ли поток фоновым. Таким образом, мы можем сделать поток приоритетным. myThread.IsBackground = false; Однако, нам необязательно создавать новый поток внутри метода Main. Давайте создадим класс который будет открывать новый поток в собственном конструкторе. Созданный нами класс по-прежнему будет заниматься счетом, но на этот раз он будет считать до указанного числа, за счет передачи потоку параметров.

using System; using System.Threading; namespace ConsoleApplication1 < class Program < class myThread < Thread thread; public myThread(string name, int num) //Конструктор получает имя функции и номер до кторого ведется счет < thread = new Thread(this.func); thread.Name = name; thread.Start(num);//передача параметра в поток >void func(object num)//Функция потока, передаем параметр < for (int i = 0;i < (int)num;i++ ) < Console.WriteLine(Thread.CurrentThread.Name + " выводит " + i); Thread.Sleep(0); >Console.WriteLine(Thread.CurrentThread.Name + " завершился"); > > static void Main(string[] args) < myThread t1 = new myThread("Thread 1", 6); myThread t2 = new myThread("Thread 2", 3); myThread t3 = new myThread("Thread 3", 2); Console.Read(); >> > 

Разберем данный пример. Конструктор нашего класса myThread принимает 2 параметра: строку, в которой мы определяем имя потока, и номер до которого будет вестись счет в цикле. В конструкторе мы создаем поток, связанный с функцией func данного объекта. Далее мы присваиваем нашему потоку имя, используя поле Name созданного потока. И запускаем наш поток, передав функции Start аргумент.

Обратите внимание на то, что функция вызываемая потоком может принимать только 1 аргумент, и только типа object. В функции func цикл for досчитывает до числа, переданного ему как аргумент, и поток завершается. В функции Main мы создаем 3 пробных объекта, работа которых выведет на консоль примерно следующий текст.

Thread 1 выводит 0
Thread 1 выводит 1
Thread 2 выводит 0
Thread 2 выводит 1
Thread 2 выводит 2
Thread 2 завершился

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

Потоки и работа с ними

Многопоточность позволяет увеличивать скорость реагирования приложения и, если приложение работает в многопроцессорной или многоядерной системе, его пропускную способность.

Процессы и потоки

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

По умолчанию программа .NET запускается с одним потоком, часто называемым основным потоком. Тем не менее она может создавать дополнительные потоки для выполнения кода параллельно или одновременно с основным потоком. Эти потоки часто называются рабочими потоками.

Цели применения нескольких потоков

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

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

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

Как использовать многопоточность в .NET

Начиная с .NET Framework 4, для многопоточности рекомендуется использовать библиотеку параллельных задач (TPL) и Parallel LINQ (PLINQ). Дополнительные сведения см. в разделе Параллельное программирование.

Библиотека параллельных задач и PLINQ полагаются на потоки ThreadPool. Класс System.Threading.ThreadPool предоставляет приложения .NET с пулом рабочих потоков. Также можно использовать потоки из пула потоков. Дополнительные сведения см. в разделе Управляемый пул потоков.

Наконец, можно использовать класс System.Threading.Thread, который представляет управляемый поток. Дополнительные сведения см. в разделе Использование потоков и работа с потоками.

Несколько потоков могут требовать доступ к общему ресурсу. Чтобы сохранить ресурс в непроверенном состоянии и избежать условий гонки, необходимо синхронизировать доступ к потоку к нему. Вы также можете координировать взаимодействие нескольких потоков. Платформа .NET предоставляет ряд типов для синхронизации доступа к общему ресурсу или координации взаимодействия потоков. Дополнительные сведения см. в разделе Обзор примитивов синхронизации.

Исключения следует обрабатывать в потоках. Необработанные исключения в потоках, как правило, приводят к завершению процесса. Дополнительные сведения см. в статье Исключения в управляемых потоках.

См. также

  • Объекты и функциональные возможности работы с потоками
  • Рекомендации по работе с потоками
  • Процессы и потоки
  • Параллельная обработка в .NET
  • Асинхронные шаблоны программирования в .NET

Использование потоков и работа с потоками

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

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

Если требуется больший контроль над поведением потоков приложения, можно управлять потоками самостоятельно. Однако многопоточное программирование значительно упрощается с System.Threading.Tasks.Parallel помощью классов и System.Threading.Tasks.Task , Parallel LINQ (PLINQ), параллельных классов коллекций в System.Collections.Concurrent пространстве имен и модели программирования, основанной на концепции задач, а не потоков. Дополнительные сведения см. в статье Параллельное программирование в .NET и Библиотека параллельных задач (TPL).

Практическое руководство. Создание и запуск нового потока

Новый поток создается путем создания нового экземпляра System.Threading.Thread класса . В конструкторе указывается имя метода, который необходимо выполнить в новом потоке. Чтобы запустить созданный поток, вызовите метод Thread.Start. Дополнительные сведения и примеры см. в статье Создание потоков и передача данных во время запуска и справочнике по API Thread.

Практическое руководство. Остановка потока

Чтобы прервать выполнение потока, используйте метод System.Threading.CancellationToken. Это единый способ совместной отмены потоков. Подробные сведения см. в статье Отмена в управляемых потоках.

Иногда невозможно совместно остановить поток, так как он выполняет сторонний код, не предназначенный для совместной отмены. В этом случае вы можете выполнить принудительное завершение. Чтобы принудительно завершить выполнение потока, воспользуйтесь методом Thread.Abort в .NET Framework. Этот метод вызывает ThreadAbortException в потоке, для которого был вызван. Дополнительные сведения см. в разделе Уничтожение потоков. Метод Thread.Abort не поддерживается в .NET Core. Если необходимо принудительно завершить выполнение стороннего кода в .NET Core, запустите его в отдельном процессе и используйте Process.Kill метод .

Недоступен System.Threading.CancellationToken до платформа .NET Framework 4. Чтобы остановить поток в более ранних версиях платформа .NET Framework, используйте методы синхронизации потоков для реализации совместной отмены вручную. Например, вы можете создать изменяемое логическое поле shouldStop и использовать его для запроса остановки кода, выполняемого потоком. Дополнительные сведения см. в справочнике по C# и в этой статье: System.Threading.Volatile.

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

Практическое руководство. Приостановка или прерывание потока

Используйте метод Thread.Sleep, чтобы приостановить текущий поток на определенное время. Заблокированный поток можно прервать путем вызова метода Thread.Interrupt. Дополнительные сведения см. в разделе Приостановка и прерывание потоков.

Свойства потока

В приведенной ниже таблице показаны некоторые свойства Thread.

Свойство. Описание
IsAlive Возвращает значение true , если поток запущен и еще не завершился нормально или прерван.
IsBackground Возвращает или задает логическое значение, которое указывает, является ли поток фоновым потоком. Фоновые потоки похожи на потоки переднего плана. Однако фоновый поток не препятствует остановке процесса. Когда обработка всех основных потоков закончена, общеязыковая среда выполнения завершает процесс, применяя метод Abort к тем фоновым потокам, которые еще продолжают существовать. См. дополнительные сведения об основных и фоновых потоках.
Name Возвращает или задает имя потока. Наиболее часто используется для обнаружения отдельных потоков при отладке.
Priority Возвращает или задает ThreadPriority значение, используемое операционной системой для определения приоритетов при планировании потоков. Дополнительные сведения см. в разделе Планирование потоков и в справочнике по ThreadPriority.
ThreadState Возвращает значение ThreadState, содержащее текущие состояния потока.

См. также

  • System.Threading.Thread
  • Потоки и работа с потоками
  • Параллельное программирование

При подготовке материала использовались источники:
https://habr.com/ru/articles/126495/
https://learn.microsoft.com/ru-ru/dotnet/standard/threading/threads-and-threading
https://learn.microsoft.com/ru-ru/dotnet/standard/threading/using-threads-and-threading

Оцените статью