2.09M
Category: programmingprogramming

Основы многопоточного программирования. Общая информация

1.

Основы многопоточного
программирования
1

2.

Потоки
ОБЩАЯ ИНФОРМАЦИЯ
2

3.

Общая информация
Пото́к выполне́ния (англ. thread — нить) —
наименьшая единица обработки, исполнение
которой может быть назначено ядром операционной
системы.
3

4.

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

5.

Общая информация
Процесс содержит как минимум один (первичный) поток.
5

6.

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

7.

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

8.

Общая информация
В большинстве OS используется
алгоритм диспетчеризации.
вытесняющий
8

9.

Общая информация
В основе многих вытесняющих алгоритмов планирования
лежит концепция квантования. В соответствии с этой
концепцией каждому потоку поочередно для выполнения
предоставляется ограниченный непрерывный период
процессорного времени — квант.
9

10.

Общая информация
Смена активного потока происходит, если:
- поток завершился;
- произошла ошибка;
- поток перешел в состояние ожидания;
- исчерпан квант процессорного времени, отведенный
данному потоку.
10

11.

Общая информация
Поток завершен или
произошла ошибка
Активное состояние
(Выполнение)
Поток обратился
к устройству
ввода/вывода
Поток получил квант
времени
Поток израсходовал
квант времени
Готовность
Ожидание
Ожидание завершено
11

12.

Общая информация
У каждого потока есть свой приоритет
Операционная система планирует к исполнению более
приоритетные потоки или выделяет приоритетным потокам
больший квант времени.
12

13.

Общая информация
Потоки получают приоритеты на базе классов приоритета
своих процессов.
13

14.

Общая информация
При создании потока его приоритет по умолчанию
устанавливается равным приоритету процесса.
Приоритеты потоков могут принимать значения в
интервале ±2 относительно базового приоритета процесса.
14

15.

Общая информация
При создании потока его приоритет по умолчанию
устанавливается равным приоритету процесса.
Приоритеты потоков могут принимать значения в
интервале ±2 относительно базового приоритета процесса.
15

16.

Общая информация
Уровни приоритета потоков (в порядке убывания):
Highest
Above normal
Normal
Below normal
Lowest
16

17.

Потоки
ПРОСТРАНСТВО ИМЕН SYSTEM.THREADING
17

18.

Пространство имен System.Threading
Классы,
поддерживающие
программирование, определены
имен System.Threading.
многопоточное
в пространстве
18

19.

Пространство имен System.Threading
Interlocked Этот тип обеспечивает элементарные операции
для переменных, которые совместно используются
несколькими потоками.
Monitor Этот тип обеспечивает синхронизацию потоковых
объектов, используя блокировки и ожидания / сигналы.
Ключевое слово C# lock использует скрытый объект Monitor.
Mutex Этот примитив синхронизации можно использовать
для синхронизации между границами домена приложения.
ParameterizedThreadStart Этот делегат позволяет потоку
вызывать методы, которые принимают любое количество
аргументов.
19

20.

Пространство имен System.Threading
Semaphore Этот тип позволяет вам ограничить число потоков,
которые могут одновременно обращаться к ресурсу или
определенному типу ресурса.
Thread Этот тип представляет поток, который выполняется в
CLR. Используя этот тип, вы можете создавать
дополнительные потоки в исходном домене приложения.
ThreadPool Этот тип позволяет вам взаимодействовать с
поддерживаемым CLR пулом потоков в данном процессе.
ThreadPriority Это перечисление представляет уровень
приоритета потока (Самый высокий, Нормальный и т.д.).
20

21.

Пространство имен System.Threading
ThreadStart Этот делегат используется для указания метода,
вызываемого для данного потока. В отличие от делегата
ParameterizedThreadStart, цели ThreadStart всегда должны
иметь один и тот же прототип.
ThreadState Это перечисление определяет допустимые
состояния, которые может принимать поток (работает,
прерван и т.д.).
Timer Этот тип предоставляет механизм для выполнения
метода через определенные промежутки времени.
TimerCallback Этот тип делегата используется вместе с типами
Timer.
21

22.

Потоки
КЛАСС THREAD
22

23.

Класс Thread
Класс Thread представляет объектно-ориентированную
оболочку для заданного потока выполнения.
23

24.

Класс Thread
Класс Thread также определяет ряд методов (как
статических, так и на уровне экземпляров класса), которые
позволяют создавать новые потоки в приложении, а также
приостанавливать,
возобновлять,
останавливать
и
уничтожать определенный поток.
24

25.

Методы и свойства класса Thread
Sleep() - Этот метод (статический) приостанавливает
текущий поток на указанное время. Когда поток
приостановлен, он не использует процессорное время.
IsAlive Возвращает логическое значение, которое
указывает, был ли этот поток запущен (и еще не
завершен или не прерван).
Name Позволяет установить понятное текстовое имя
темы.
25

26.

Методы и свойства класса Thread
Priority Получает или задает приоритет потока, которому
может быть присвоено значение из перечисления
ThreadPriority.
ThreadState Получает состояние этого потока, которому
может быть присвоено значение из перечисления
ThreadState.
Abort() Указывает CLR прекратить поток.
Join() Блокирует вызывающий поток до тех пор, пока не
закончится указанный поток (тот, в котором вызывается
Join ()).
26

27.

Методы и свойства класса Thread
Resume() Возобновляет поток, который был ранее
приостановлен.
Start() Указывает CLR выполнить поток.
Suspend() Приостановляет поток. Если поток уже
приостановлен, вызов Suspend() не имеет никакого
эффекта.
27

28.

Класс Thread. Создание и запуск потока
Конструктор потока:
public Thread(ThreadStart точка_входа)
точка_входа — это имя метода, вызываемого с целью
начать выполнение потока
ThreadStart — делегат, определенный в среде .NET
Framework:
public delegate void ThreadStart ()
28

29.

Класс Thread. Создание и запуск потока
Созданный поток не начнет выполняться до тех пор,
пока не будет вызван его метод Start().
29

30.

Класс Thread. Создание и запуск потока
class MyThread
{
public int Count;
string thrdName;
public MyThread(string name)
{
Count = 0;
thrdName = name;
}
30

31.

Класс Thread. Создание и запуск потока
/// <summary>
/// Функция потока
/// </summary>
public void Run()
{
Console.WriteLine(thrdName + " начат.");
do
{
// Длительное вычисление
Thread.Sleep(500); // ждать 500 мс
Console.WriteLine($"В потоке {thrdName}, Count = {Count}");
Count++;
}
while (Count < 10);
Console.WriteLine(thrdName + " завершен.");
}
}
}
31

32.

Класс Thread. Создание и запуск потока
Console.WriteLine("Основной поток начат.");
// Создать объект типа MyThread.
MyThread mt = new MyThread("Потомок #1");
// Создать поток из метода Run объекта MyThread.
Thread newThrd = new Thread(mt.Run);
// Начать выполнение потока.
newThrd.Start();
// параллельно выводить индикацию о выполнении
do
{
Console.Write(".");
Thread.Sleep(100);
}
while (mt.Count != 10);
Console.WriteLine("Основной поток завершен");
32

33.

Класс Thread. Создание и запуск потока
Console.WriteLine("Основной поток начат.");
// Создать объект типа MyThread.
MyThread mt = new MyThread("Потомок #1");
// Создать поток из метода Run объекта MyThread.
Thread newThrd = new Thread(mt.Run);
// Начать выполнение потока.
newThrd.Start();
// параллельно выводить индикацию о выполнении
for (int i = 0; i < 20;i++)
{
Console.Write(".");
Thread.Sleep(100);
};
newThrd.Join();
Console.WriteLine("Основной поток завершен");
33

34.

Класс Thread. Создание и запуск потока
Запуск потока при создании класса MyThread:
public MyThread(string name)
{
Count = 0;
Thrd = new Thread(this.Run);
Thrd.Name = name; // задать имя потока
Thrd.Start(); // начать поток
}
34

35.

Класс Thread. Создание и запуск потока
Если потоку нужно передать параметры, то они передается
потоку в следующей форме метода Start ():
public void Start(object arg)
35

36.

Класс Thread. Создание и запуск потока
public MyThread(string name, int max)
{
Count = 0;
Thrd = new Thread( new ParameterizedThreadStart(this.Run));
Thrd.Name = name; // задать имя потока
Thrd.Start(max); // начать поток
}
36

37.

Класс Thread. Создание и запуск потока
/// <summary>
/// Функция потока
/// </summary>
void Run(object max)
{
var limit = (int)max;
Console.WriteLine(Thrd.Name + " начат.");
do
{
Thread.Sleep(500);
Console.WriteLine("В потоке " + Thrd.Name +
", Count = " + Count);
Count++;
}
while (Count < limit);
Console.WriteLine(Thrd.Name + " завершен.");
}
37

38.

Класс Thread. Пул потоков
Создание потоков требует времени. Если есть различные
короткие задачи, подлежащие выполнению, можно создать
набор потоков заранее и затем просто отправлять
соответствующие запросы
38

39.

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

40.

Класс Thread. Пул потоков
Для управления пулом потоков используется класс
ThreadPool
40

41.

Класс Thread. Пул потоков
Чтобы запросить обработку задачи потоком в пуле
потоков, вызовите метод QueueUserWorkItem.
Этот метод принимает в качестве параметра ссылку на
метод или делегат, который будет вызываться потоком,
выбранным из пула потоков.
41

42.

Класс Thread. Пул потоков
// Постановка задачи в очередь
ThreadPool.QueueUserWorkItem(ThreadProc);
// Процедура потока
static void ThreadProc(Object stateInfo)
{
// код задачи
}
42

43.

Класс Thread. Пул потоков
Пул потоков управляет потоками эффективно, уменьшая
количество создаваемых, запускаемых и останавливаемых
потоков.
43

44.

Класс Thread. Пул потоков (ограничения)
Все потоки в пуле потоков являются фоновыми. Сделать поток
из пула приоритетным не удастся.
Нельзя изменять приоритет или имя находящего в пуле
потока.
Потоки в пуле подходят для выполнения только коротких
задач. Если необходимо, чтобы поток функционировал все
время (как, например, поток средства проверки орфографии в
Word), его следует создавать с помощью класса Thread.
Созданный поток невозможно прерывать или находить по
имени.
44

45.

Потоки
СИНХРОНИЗАЦИЯ ПОТОКОВ
45

46.

Синхронизация потоков
Во
многопоточных
приложениях
потоки
могут
использовать общие разделяемые ресурсы: процессорное
время, память, файлы, переменные.
46

47.

Синхронизация потоков
Например: все потоки в домене приложений имеют
одновременный доступ к общим данным приложения.
Может возникнуть состояние гонок, когда поток А будет
считывать данные, в то время как поток B будет изменять
эти данные
47

48.

Синхронизация потоков
Какие данные прочитает поток А – до изменения или
после? И какие из этих значений считать правильными?
Дело в том, что мы точно не знаем, в какой
последовательности планировщик потоков будет выделять
кванты времени для каждого потока.
48

49.

Синхронизация потоков
public class ConcurrentThread
{
Random r = new();
public void PrintNumbers()
{
for (int i = 0; i < 10; i++)
{
// Приостановить поток на случайное время
Thread.Sleep(100 * r.Next(5));
Console.Write($"{i}, ");
}
Console.WriteLine();
}
}
49

50.

Синхронизация потоков
var p = new ConcurrentThread();
// Создать 10 потоков
Thread[] threads = new Thread[10];
for (int i = 0; i < 10; i++)
{
threads[i] =
new Thread(p.PrintNumbers);
}
// Запустить все потоки
foreach (Thread t in threads)
t.Start();
Console.ReadLine();
50

51.

Синхронизация потоков
Порядок выполнения
непредсказуем
созданных
потоков
абсолютно
51

52.

Синхронизация потоков
Для синхронизации работы потоков используются разные
объекты – Monitor, Mutex, Semaphore и др.
52

53.

Синхронизация потоков (критическая секция)
Ключевое слово lock позволяет определить область
операторов (критическую секцию), которые должны быть
синхронизированы между потоками.
lock(lockObj)
{синхронизируемые операторы }
53

54.

Синхронизация потоков (критическая секция)
public class ConcurrentThread
{
object locker = new object();
Random r = new Random();
public void PrintNumbers()
{
lock(locker)
for (int i = 0; i < 10; i++)
{
// Приостановить поток на случайное время
Thread.Sleep(100 * r.Next(5));
Console.Write($"{i}, ");
}
Console.WriteLine();
}
}
54

55.

Синхронизация потоков (Monitor)
Конструкция оператора lock инкапсулирует
синтаксис использования мониторов.
в
55
себе

56.

Синхронизация потоков (Monitor)
Monitor.Enter(locker);
try
{
for (int i = 0; i < 10; i++)
{
// Приостановить поток на случайное время
}
Thread.Sleep(100 * r.Next(5));
Console.Write($"{i}, ");
}
finally
{
Monitor.Exit(locker);
}
56

57.

Синхронизация потоков (Mutex)
Kласс
Mutex
является
классом-оберткой
соответствующим объектом ОС Windows «Mutex»
над
Особенностью
мьютекса
является
возможность
синхронизировать процессы. Для этого при создании
объекта нужно задать мьютексу имя.
57

58.

Синхронизация потоков (Mutex)
Mutex mutex = new Mutex(false);
Random r = new Random();
public void PrintNumbers()
{
mutex.WaitOne();
}
for (int i = 0; i < 10; i++)
{
. . .
}
mutex.ReleaseMutex();
58

59.

Синхронизация потоков (Semaphore)
Kласс Semaphore является классом-оберткой над
соответствующим объектом ОС Windows «Semaphore»
Semaphore работает аналогично классу Mutex. Отличие
заключается в том, что Mutex разрешает доступ к ресурсу
только одному потоку (процессу), в то время как Semaphore
разрешает доступ к ресурсам сразу нескольким потокам
(процессам). Количество указывается при создании объекта
Semaphore.
59

60.

Синхронизация потоков (Event)
Класс AutoResetEvent является оберткой над объектом ОС
Windows «Event» и позволяет переключить данный объектсобытие из сигнального в несигнальное состояние.
60

61.

Синхронизация потоков (AutoResetEvent)
AutoResetEvent notifier = new AutoResetEvent(true);
public void PrintNumbers()
{
notifier.WaitOne();
// . . .
notifier.Set() ;
}
Console.WriteLine();
61

62.

Неблокирующая синхронизация потоков
Класс Interlocked позволяет вызывать потокобезопасные
простые атомарные операции с переменными без
использования объектов синхронизации (Monitor, Mutex и
т.д.)
Применение класса Interlocked является гораздо более
быстрым подходом по сравнению с остальными приемами
по обеспечению синхронизации.
62

63.

Неблокирующая синхронизация потоков
Некоторые методы класса Interlocked:
Decrement(ref x)
Безопасно уменьшает значение x на 1.
Эквивалентно x--
Exchange(ref x, value)
Безопасно присваивает x значение value.
Эквивалентно x=value
Increment(ref x)
Безопасно увеличивает значение x на 1
Эквивалентно x++
Add(ref x, value)
Безопасно складывает значение x и значение value.
Эквивалентно x+=value
63

64.

Синхронизация потоков (System.Threading.Timer)
Класс Timer из пространства имен System.Threading
позволяет запускать метод по истечению заданного
периода времени.
64

65.

Синхронизация потоков (System.Threading.Timer)
static void TimerCallBackProc(Object data)
{
Console.WriteLine($"{DateTime.Now.ToLongTimeString()}");
}
Console.WriteLine("***** Timer Starts *****\n");
Timer timer = new Timer(TimerCallBackProc,null,0,1000);
Console.ReadLine();
65

66.

Синхронизация потоков (System.Timers.Timer)
Класс Timer из пространства имен System.Timers позволяет
генерировать события по истечению заданного периода
времени.
66

67.

Синхронизация потоков (System.Timers.Timer)
using st = System.Timers;
st::Timer timer = new st::Timer(1000);
timer.Elapsed+=(s,e)=>
Console.WriteLine($"{e.SignalTime.ToLongTimeString()}");
Console.WriteLine("***** Timer Starts *****\n");
timer.Start();
Console.ReadLine();
67

68.

StopWatch
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
Thread.Sleep(10000);
stopWatch.Stop();
// Get the elapsed time as a TimeSpan value.
TimeSpan ts = stopWatch.Elapsed;
// Format and display the TimeSpan value.
var
elapsed
=
$"{ts.Hours:00}:{ts.Minutes:00}:{ts.Seconds:00}:{ts.Milliseconds/
10:00}";
Console.WriteLine("RunTime " + elapsed);
https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch.-ctor?f1url=%3FappId%3DDev16IDEF1%26l%3DENUS%26k%3Dk(System.Diagnostics.Stopwatch.%2523ctor);k(DevLang-csharp)%26rd%3Dtrue&view=net-5.0
68
English     Русский Rules