Multithreading
.NET
kontur.ru
https://github.com/kontur-courses/multithread-async
Клабуков Эрик
Старший инженер-программист
Лиясов Сергей
инженер-программист
Тест по материалу подготовки
Зачем все это нужно�
Зачем это все нужно (v2)
Рендеринг интерфейса графического приложения не должен зависать:
Процесс
Представление памяти процесса
Поток выполнения (Thread)
Многозадачность
Вытесняющая многозадачность
Приоритеты потоков
Планировщик потоков
Динамический приоритет планировщика
Многоядерность
Закон Амдала
Закон Амдала гласит, что максимальное ускорение, которое можно получить от параллелизации задачи, ограничено долей задачи, которая не может быть распараллелена.
Например, если 20% задачи нельзя распараллелить (f = 0,2), то максимальное ускорение, которое можно получить с использованием бесконечного количества процессоров (n стремится к бесконечности), составляет:
S = 1 / (1 - 0,2 + 0,2/∞) = 1 / 0,2 = 5
Эксперимент (30 минут)
Напишите программу, которая вычислит размер кванта времени планировщика потоков.�Воспользуйтесь заготовкой samples/QuantumOfSwitching
�Вероятно, вам понадобится больше одного потока в своей программе.�Для более достоверного расчета, вам нужно будет искусственно привязать все потоки своего процесса к одному ядру
Примитивы синхронизации
Monitor и lock
var lockObj = new Object();
bool lockTaken = false;
try
{
Monitor.Enter(lockObj, ref lockTaken);
// Our code
}
finally
{
// Ensure that the lock is released.
if(lockTaken)
Monitor.Exit(lockObj);
}
var lockObj = new Object();
lock (lockObj)
{
// Our code
}
Thread Pool
Эксперимент (30 минут)
Напишите собственную реализацию пула потоков.�Воспользуйтесь заготовкой samples/ThreadPool
�Обойдитесь без использования
Monitor.Pulse и Monitor.Wait
Thread Pool .NET 4.0 (и выше)
Динамический размер пула потоков
Тред пул в .NET является динамическим и может управляться через
LockFree
Interlocked
Метод | Описание | Сигнатура |
Increment | Атомарно увеличивает значение на 1 | int Increment(ref int count) |
Decrement | Атомарно уменьшает значение на 1 | int Decrement(ref int count) |
Exchange | Атомарно заменяет значение и возвращает старое | object Exchange(ref object location, object value) |
CompareExchange | Заменяет значение только если текущее равно expected | object CompareExchange(ref object location, object value, object comparand) |
Эксперимент (30 минут)
Реализуйте lock free версии стека и очереди
Воспользуйтесь заготовкой samples/LockFree
�
При реализации нельзя использовать блокировки (т.к. это lock free)
А также срезать путь делая обертку над ConcurrentQueue и ConcurrentStack
Data Parallelism
Примечание: PLINQ и Parallel являются частью TPL
Но более подробнее TPL разберем далее
PLINQ
Concurrency
Order
Buffering
Buffering
MergeOptions | Порядок элементов | Производительность | Задержка |
NotBuffered | Не сохраняется | Высокая | Минимальная |
AutoBuffered | Может сохраняться | Оптимальная | Средняя |
FullyBuffered | Может сохраняться | Ниже | Максимальная |
Default | Как AutoBuffered | Оптимальная | Средняя |
Partitioning
Как добится параллелизма
Число ядер CPU > 1
Отсутствие блокировок (распараллеливаемость)
Делегаты не слишком маленькие (вычислительная сложность не превышает накладные расходы)
Сохранение порядка может убить производительность (порядок не важен)
TPL (Task Parallel Library)
Набор готовых классов и методов для более простого распараллеливания кода приложения
Поддержка асинхронного программирования которое не блокирует исполнение приложения (потоки возвращаются в пул и выполняют полезную работу)
Дает инструменты по управлению отменой, управлению состоянием и получению результатов
Появился в .NET Framework 4.0 (2010 год)
Класс Task
Что еще дает Task?
Task не только содержит информацию о задаче
которую мы отправили на выполнение в пул потоков, но и предоставляет дополнительные средства для:
Получение результата и исключения до TPL
Как это стало после появления TPL
Async/Await
Эксперимент (30 минут)
Есть простая реализация NMAP где используется синхронный последовательный обход набора ip адресов с проверкой их доступности.�Необходимо реализовать `AsyncScanner` сделав проверку параллельной и асинхронной
Воспользуйтесь заготовкой samples/NMAP
�
Все методы должны быть асинхронными (Значит возвращать Task<T>)
Для распараллеливания использовать Parallel.ForEachAsync
Использовать асинхронные перегрузки у методов за место� синхронных (Оканчиваются на Async или возвращают Task)
�
Эксперимент (совместный)
Посмотрим на реализацию ClusterServer, режим синхронной и асинхронной обработки поступающих запросов
Запустим нагрузочное тестирование, чтобы посмотреть на скорость обработки запросов и пропускную способность синхронного и асинхронного режимов
Посмотрим в реальном времени, как нагружается ThreadPool в синхронном
и асинхронном режимах
Эксперимент (1 час 20 минут)
Задача: Написать децентрализованный чат, который будет сам искать клиентов в сети ( на указанных ip адресах), подключаться друг к другу (каждый к каждому) и пересылать сообщения между клиентами
Воспользуйтесь заготовкой samples/HackChat
�
Список ip адресов может быть захардкожен (не обязательно принимать
в аргументах)
На Windows/Linux можно локально запускать экземпляры на адресах
127.0.0.1 , 127.0.0.2, 127.0.0.3 для имитации нескольких клиентов
На MacOS такое не сработает, там можно разойтись только по
портам и использовать можно только 127.0.0.1
Не забудьте проверять, что клиент к кому вы подключаетесь уже не подключился к вам сам), как и защиту от подключения к самому себе
Домашнее задание
Необходимо реализовать 3 стратегии обхода реплик для ClusterClient:
Подробное описание домашнего задания в репозитории
в homework 2/README.md
В проекте ClusterTests находятся соответствующие клиентам тесты, реализации клиентов должны стабильно проходить все соответствующие тесты.
Благодарю
за внимание!
Вопросы?
Эрик Клабуков
Старший инженер-программист
kontur.ru
Дополнительный материал