C# Многопоточность

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

Поток — этоЛегковесный процессРаспространённым примером использования потоков является реализация параллельного программирования в современных операционных системах. Использование потоков позволяет избежать напрасной траты циклов CPU и повышает эффективность приложений.

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

Жизненный цикл потока

Жизненный цикл потока начинается, когда создается объект класса System.Threading.Thread, и заканчивается, когда поток завершается или завершает выполнение.

Различные состояния в жизненном цикле потока перечислены ниже:

  • Незапущенное состояние: состояние, когда экземпляр потока создан, но метод Start не был вызван.
  • Состояние готовности: состояние, когда поток готов к выполнению и ожидает цикла процессора.
  • Неисполняемое состояние: Поток не может выполняться в следующих случаях:
    • Был вызван метод Sleep
    • Был вызван метод Wait
    • Заблокирован операцией ввода-вывода
  • Состояние завершения: состояние, когда поток завершил выполнение или был прерван.

главный поток

В C#,System.Threading.ThreadКласс Thread используется для работы с потоками. Он позволяет создавать отдельные потоки и получать доступ к ним в многопоточном приложении. Первый поток, выполняемый в процессе, называется главным потоком.главный поток。

Когда программа на C# начинает выполнение, главный поток создается автоматически. ИспользуяThread, потоки, созданные классом, называются дочерними потоками главного потока. Вы можете использовать свойствоCurrentThreadкласса Thread для доступа к потоку.

Следующая программа демонстрирует выполнение главного потока:

Пример

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class MainThreadProgram
    {
        static void Main(string[] args)
        {
            Thread th = Thread.CurrentThread;
            th.Name = "MainThread";
            Console.WriteLine("This is {0}", th.Name);
            Console.ReadKey();
        }
    }
}

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

This is MainThread

Общие свойства и методы класса Thread

В таблице ниже перечисленыThreadнекоторые общие методы классаProperty:

СвойствоОписание
CurrentContextВозвращает текущий контекст, в котором выполняется поток.
CurrentCultureВозвращает или задает культуру текущего потока.
CurrentPrincipalВозвращает или задает текущий субъект потока (для безопасности на основе ролей).
CurrentThreadВозвращает текущий выполняющийся поток.
CurrentUICultureВозвращает или задает текущую культуру, используемую диспетчером ресурсов для поиска ресурсов, зависящих от культуры, во время выполнения.
ExecutionContextВозвращает объект ExecutionContext, содержащий контекстную информацию о текущем потоке.
IsAliveВозвращает значение, указывающее состояние выполнения текущего потока.
IsBackgroundВозвращает или задаёт значение, указывающее, является ли поток фоновым.
IsThreadPoolThreadВозвращает значение, указывающее, принадлежит ли поток управляемому пулу потоков.
ManagedThreadIdВозвращает уникальный идентификатор текущего управляемого потока.
NameВозвращает или задаёт имя потока.
ПриоритетВозвращает или задаёт значение, указывающее приоритет планирования потока.
ThreadStateВозвращает значение, содержащее состояние текущего потока.

В таблице ниже перечисленыThreadнекоторые распространённые методы классаМетоды:

Порядковый номерНазвание метода & описание
1public void Abort()
Вызывает ThreadAbortException в потоке, в котором вызывается этот метод, чтобы начать процесс завершения этого потока. Вызов этого метода обычно завершает поток.
2public static LocalDataStoreSlot AllocateDataSlot()
Выделяет неименованный слот данных во всех потоках. Для повышения производительности вместо этого используйте поля, помеченные атрибутом ThreadStaticAttribute.
3public static LocalDataStoreSlot AllocateNamedDataSlot( string name)
Выделяет именованный слот данных во всех потоках. Для повышения производительности вместо этого используйте поля, помеченные атрибутом ThreadStaticAttribute.
4public static void BeginCriticalRegion()
Уведомляет хост о том, что выполнение собирается войти в область кода, в которой последствия прерываний потоков или необработанных исключений могут поставить под угрозу другие задачи в домене приложения.
5public static void BeginThreadAffinity()
Уведомляет хост о том, что управляемый код собирается выполнить инструкции, которые зависят от идентификатора текущего физического потока операционной системы.
6public static void EndCriticalRegion()
Уведомляет хост о том, что выполнение собирается войти в область кода, в которой прерывания потоков или необработанные исключения влияют только на текущую задачу.
7public static void EndThreadAffinity()
Уведомляет хост о том, что управляемый код завершил выполнение инструкций, зависящих от идентификатора текущего физического потока операционной системы.
8public static void FreeNamedDataSlot(string name)
Удаляет связь между именами и слотами для всех потоков в процессе. Для повышения производительности вместо этого используйте поля, помеченные атрибутом ThreadStaticAttribute.
9public static Object GetData( LocalDataStoreSlot slot )
Извлекает значение из указанного слота в текущем потоке в пределах текущего домена текущего потока. Для повышения производительности вместо этого используйте поля, помеченные атрибутом ThreadStaticAttribute.
10public static AppDomain GetDomain()
Возвращает текущий домен, в котором выполняется текущий поток.
11public static AppDomain GetDomainID()
Возвращает уникальный идентификатор домена приложения.
12public static LocalDataStoreSlot GetNamedDataSlot( string name )
Выполняет поиск именованного слота данных. Для повышения производительности вместо этого используйте поля, помеченные атрибутом ThreadStaticAttribute.
13public void Interrupt()
Прерывает поток, находящийся в состоянии потока WaitSleepJoin.
14public void Join()
Блокирует вызывающий поток до завершения потока, продолжая при этом выполнять стандартную перекачку сообщений COM и SendMessage. Этот метод имеет различные перегруженные формы.
15public static void MemoryBarrier()
Синхронизирует доступ к памяти следующим образом: процессор, выполняющий текущий поток, не может переупорядочивать инструкции так, чтобы обращения к памяти после вызова MemoryBarrier выполнялись до обращений к памяти, предшествовавших вызову MemoryBarrier.
16public static void ResetAbort()
Отменяет Abort, запрошенный для текущего потока.
17public static void SetData( LocalDataStoreSlot slot, Object data )
Задает данные в указанном слоте текущего выполняющегося потока для текущего домена потока. Для повышения производительности вместо этого используйте поля, помеченные атрибутом ThreadStaticAttribute.
18public void Start()
Запускает поток.
19public static void Sleep( int millisecondsTimeout )
Приостанавливает поток на некоторое время.
20public static void SpinWait( int iterations )
Заставляет поток ожидать в течение времени, определяемого параметром iterations.
21public static byte VolatileRead( ref byte address )
public static double VolatileRead( ref double address )
public static int VolatileRead( ref int address )
public static Object VolatileRead( ref Object address )

Читает поле. Независимо от количества процессоров или состояния процессора, значение является последним значением, записанным любым процессором в компьютере. Этот метод имеет различные перегрузки. Здесь приведены только некоторые формы.
22public static void VolatileWrite( ref byte, byte value )
public static void VolatileWrite( ref double address, double value )
public static void VolatileWrite( ref int address, int value )
public static void VolatileWrite( ref Object address, Object value )

Немедленно записывает значение в поле, чтобы значение было видно всем процессорам в компьютере. Этот метод имеет различные перегруженные формы. Здесь приведены только некоторые формы.
23public static bool Yield()
Заставляет вызывающий поток выполнить другой поток, готовый к запуску на текущем процессоре. Поток, который должен быть выполнен, выбирается операционной системой.

Создание потоков

Потоки создаются путем расширения класса Thread. Расширенный класс Thread вызывает методStart()для запуска выполнения дочернего потока.

Следующая программа демонстрирует эту концепцию:

Пример

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
        }
       
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

In Main: Creating the Child thread
Child thread starts

Управление потоками

Класс Thread предоставляет различные методы для управления потоками.

В следующем примере демонстрируетсяsleep()использование метода для приостановки потока на заданное время.

Пример

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            Console.WriteLine("Child thread starts");
            // Приостановить поток на 5000 миллисекунд
            int sleepfor = 5000;
            Console.WriteLine("Дочерний поток приостановлен на {0} секунд",
                              sleepfor / 1000);
            Thread.Sleep(sleepfor);
            Console.WriteLine("Дочерний поток возобновляет работу");
        }
       
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            Console.ReadKey();
        }
    }
}

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

In Main: Creating the Child thread
Child thread starts
Child Thread Paused for 5 seconds
Child thread resumes

Уничтожение потоков

Abort()— метод, используемый для уничтожения потока.

При выбросеthreadabortexceptionпоток прерывается во время выполнения. Это исключение невозможно перехватить. Если имеетсяfinallyблок, управление передается вfinallyБлок.

Следующая программа иллюстрирует это:

Пример

using System;
using System.Threading;

namespace MultithreadingApplication
{
    class ThreadCreationProgram
    {
        public static void CallToChildThread()
        {
            try
            {

                Console.WriteLine("Child thread starts");
                // Считать до 10
                for (int counter = 0; counter <= 10; counter++)
                {
                    Thread.Sleep(500);
                    Console.WriteLine(counter);
                }
                Console.WriteLine("Дочерний поток завершён");

            }
            catch (ThreadAbortException e)
            {
                Console.WriteLine("Исключение прерывания потока");
            }
            finally
            {
                Console.WriteLine("Не удалось перехватить исключение потока");
            }

        }
       
        static void Main(string[] args)
        {
            ThreadStart childref = new ThreadStart(CallToChildThread);
            Console.WriteLine("In Main: Creating the Child thread");
            Thread childThread = new Thread(childref);
            childThread.Start();
            // Приостановить основной поток на некоторое время
            Thread.Sleep(2000);
            // Теперь прервать дочерний поток
            Console.WriteLine("В Main: прерывание дочернего потока");
            childThread.Abort();
            Console.ReadKey();
        }
    }
}

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

In Main: Creating the Child thread
Child thread starts
0
1
2
In Main: Aborting the Child thread
Thread Abort Exception
Couldn't catch the Thread Exception 
Другие расширения