1 / 73

Разработка многопоточных приложений на .Net под Windows: основы

Разработка многопоточных приложений на .Net под Windows: основы. Михаил Дутиков CUSTIS ноябрь 2011 года. Почему именно я об этом рассказываю ?. Речь пойдет о следующем :  - Потоки в Windows, кванты , приоритеты , переключения контекста , планировщик .

ila-pate
Download Presentation

Разработка многопоточных приложений на .Net под Windows: основы

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Разработка многопоточных приложений на .Net под Windows: основы Михаил Дутиков CUSTIS ноябрь 2011 года

  2. Почемуименно я обэтомрассказываю? Речьпойдет о следующем:  - Потоки в Windows, кванты, приоритеты, переключенияконтекста, планировщик.  - Потоки в .Net. Явноесоздание и взаимодействие.  - Архитектурамногопоточныхприложений (уровеньклассов). Управлениесостоянием. Изоляция, неизменяемость, синхронизация.  - Синхронизация: объектыядра, пользовательскогорежима, гибриды. Сценариииспользования.  - Потокобезопасныйкод. Примерагента и потокобезопаснойобертки.  - Пулпотоков.  - Таймеры;  - Исключения;

  3. Процессы и потоки в Windows -- Капитан Очевидность Windows многозадачна -- в ней работает несколько процессов одновременно. Грубо говоря, процесс -- экземпляр программы. Процессы изолированы, не имеют доступа к адресным пространствам друг друга. Внутри процессов всю работу выполняют потоки. Поток, грубо говоря, -- виртуальный процессор в том смысле, что исполняет код программы на настоящем (невиртуальном) процессоре.

  4. Процессорное время: планировщик - Как Windows распределяет процессорное время между потоками? - Использует вытесняющую многопоточность. Типичный сценарий: поток, если нет потоков более приоритетных, но есть потоки с равным ему приоритетом, исполняет код на ядре процессора в течение кванта времени. Затем это ядро достается другому потоку -- происходит переключение контекста.

  5. Процессорное время: планировщик • Планировщик игнорирует процессы, раздает ядра только потокам. • Планировщик гарантирует: поток с самым высоким приоритетом, готовый к работе, сейчас выполняется. • Потокам с одинаковым приоритетом при прочих равных достается примерно одинаково процессорного времени.

  6. Приоритет Число от 0 до 31. Диапазон от 1 до 15 -- динамический. От 16 до 31 -- реального времени. У процесса есть "класс приоритета": Idle (4), Below Normal (6), Normal (8), Above Normal (10), High (13), Realtime (24). Приоритет потока задается относительно класса того процесса, которому поток принадлежит. Относительный приоритет потока, где P - приоритет процесса: Idle (нет в .Net), Lowest (P-2), Below Normal (P-1), Normal (P), Above Normal (P+1), Highest (P+2), Time-critical (нет в .Net).

  7. Приоритеты

  8. Приоритеты: использовать? Если ими вообще пользоваться, то лучше избегать классов High и Realtime. Высокоприоритетным потокам лучше всего давать короткие задачи. Лучше снижать приоритеты потокам, чем повышать.

  9. Когда Windows повышает приоритеты потокам (самые важные случаи) - всем потокам в системе, которые не работали 4 секунды или больше, приоритет временно повышается до 15. - поток, ждавший и дождавшийся события через объект ядра (о них --дальше), получает +1 к приоритету. Бонусы Windows дает на время. Как она их отбирает?

  10. Переключение контекста При переключении контекста Windows: • сохраняет содержимое регистров и другие данные в контекст потока (в объекте ядра ОС); • переключает виртуальное адресное пространство, если новый поток из другого процесса; • загружает контекст того потока, который вот-вот получит процессорное время (наполняет регистры процессора и проч.);

  11. Переключение контекста: чем плохо?  - само по себе переключение -- это чистый оверхед;  - остывают процессорные кэши, падает производительность; Для борьбы с проблемой №2 Windows назначает потоку "идеальный процессор" при создании и запоминает, на каком процессоре поток выполнялся в последний раз. У этих двух процессоров больше шансов получить этот же поток снова, чем у остальных.

  12. Кванты Чему равен квант? На клиентских версиях Windows (XP, 2000, Vista, 7) по умолчанию -- примерно 2 интервала между срабатываниями системного таймера. На серверных версиях ОС -- 12 интервалов. На большей части железа этот интервал составляет 10-15 мс. Почему такая разница для клиентских и серверных систем? - 2 способа поменять квант - - подхачить реестр Windows утраивает квант (т.н. quantum boost) для потоков, принадлежащих foreground-процессу (если выбрано "программ" в настройках системы -- см. картинку).

  13. Где прочесть больше о планировщике Windows и потоках с точки зрения ОС

  14. Каковы ваши вопросы по вводной части?

  15. Многопоточные приложения: зачем? •  более эффективное использование ресурсов компьютера; •  повышение производительности; •  повышение юзабилити (в UI); •  более удобная программная модель (редко);

  16. Многопоточные приложения: почему нет? Их гораздо сложнее разрабатывать чем однопоточные:  - у них более сложный control flow;  - их отладка мало что дает в смысле поиска багов;  - их сложнее тестировать;  - состояние системы/компонента/класса труднее держать консистентным, если оно меняется больше чем одним потоком одновременно;  - добавляют уникальные классы проблем -- race condition, deadlock и проч.;

  17. Потоки в .Net: знакомимся ближе Поток -- объект ядра ОС. Помимо прочего хранит контекст исполнения. Поток в .Net -- скромная обертка над этим объектом. Потоку выделен стек пользовательского режима -- 1 Мб. И стек режима ядра -- 12 Кб (32-битная Windows), 24 Кб (64-битная). Еще есть другие структуры... Состояния: Unstarted, Running, WaitSleepJoin, Suspended, Stopped, Aborted.

  18. Создание потока: явное (пример) Foreground-поток. 1 Мб стека.

  19. Потоки: уничтожение 4 способа уничтожить поток: - выход из метода потока; - необработанные исключения (finally-блоки из стека отрабатывают); - Thread.Abort(); - завершение процесса (background-потоки);

  20. Поток как объект Что можно делать с потоком, созданным явно  - Start();  - Join();  - Abort(); // не лучшая идея в общем случае  - Priority;  - CurrentCulture;  - CurrentUICulture;  - IsThreadPoolThread;  - IsBackground;  - ... (остальное не рассматриваем)  - Thread.Sleep(...); // зависит от точности таймера, нагрузки на систему  - Thread.ResetAbort(...); // нужны права  - Thread.CurrentThread;  - ... (остальное не рассматриваем)

  21. Потоки: взаимодействие (пример с багом)

  22. Архитектура: управление состоянием в многопоточной среде Общее состояние часто неизбежно. Три главных приема для управления состоянием: - неизменяемое общее состояние; - изоляция; - синхронизация (потоки координируют свои действия в отношении общего состояния);

  23. Архитектура: управление состоянием, неизменяемость "Неизменяемые" объекты могут быть: - поверхностно неизменяемыми; - глубоко неизменяемыми; Также полезны условно неизменяемые типы.

  24. Архитектура: управление состоянием, изоляция - процессы; - домены; - соглашения; Пример изоляции. Агент. Его свойства: 1) состояние изолировано 2) общается с миром при помощи асинхронных сообщений 3) слабо связан с другими агентами. Пример агента сегодня будет в виде асинхронного логгера.

  25. Архитектура: управление состоянием, синхронизация Синхронизация почти неизбежна. Дальше -- подробнее о синхронизации.

  26. Синхронизация: условная классификация Синхронизация контроля (как вариант: поток А ждет, пока поток Б сделает что-то нужное потоку А). Синхронизация доступа к данным.

  27. Синхронизация: объекты ядра Windows, общие сведения Как ждать событий от других потоков? Busy wait? while (!predicate) {  BusyWait; } Для длительного ожидания нужны вещи поэффективнее! Как работают объекты ядра? 2 состояния: сигнальное и несигнальное. kernelObject.WaitSignal(...);

  28. Синхронизация: объекты ядра Windows, общие сведения Объекты ядра:  - хороши для длительных периодов ожидания;  - могут использоваться для межпроцессной синхронизации;  - не слишком быстры, тяжеловесны (не забывайте о Dispose!);  - нечестны (не гарантируют FIFO);

  29. Синхронизация: объекты ядра Windows -- WaitHandle Базовый класс для всех .Net- оберток к объектам ядра, служащим для синхронизации. На вызове Wait*(...) может блокироваться любое число потоков. * -- waitHandles: не больше 64 штук.

  30. Синхронизация. Объекты ядра. Мьютекс Mutex = Mutual exclusion. Именно это основной сценарий использования. Пример использования:

  31. Синхронизация. Объекты ядра. Мьютекс. Чиним race condition

  32. Синхронизация. Объекты ядра. Семафор Служит для ограничения доступа к пулу ресурсов. Дает одновременный доступ не больше чем N интересующимся (N указывается при создании семафора). Остальные ждут. Потокобезопасный счетчик. - WaitOne(...); - Release(...); Не привязывается к потокам, как мьютекс.

  33. Синхронизация. Объекты ядра. Семафор: пример

  34. Синхронизация. Объекты ядра. ManualResetEvent Когда сигналит, пропускает любое количество ждущих потоков. Метафора для сигнального состояния -- распахнутые ворота.

  35. Синхронизация. Объекты ядра. AutoResetEvent Когда сигналит, пропускает строго один ждущий поток. Метафора для сигнального состояния -- шлюз офиса CUSTIS в Архангельском пер.

  36. Синхронизация. Объекты ядра. События и баги Значительная часть багов синхронизации, что я исправлял в production-коде разных команд, была связана с событиями. События не запоминают, сколько раз вы вызываете Set(...). Если вызываете Set(...), то точно не известно, в какой момент времени будут отпущены все потоки, ждущие события. Bottom line: лучше используйте ManualResetEvent для однократного ожидания (Set => отпускаем всех ждущих, Reset не трогаем).

  37. Синхронизация. Объекты ядра. Потоки и ожидание Не обязательно создавать поток только чтобы ждать какого-то события (потоки дороги). Пул потоков это отлично сделает за вас. См. ThreadPool.RegisterWaitForSingleObject(...). К пулу мы еще сегодня вернемся :)

  38. Синхронизация. Пользовательский режим - Interlocked-методы; - Thread.VolatileRead(...); [не сегодня] - Thread.VolatileWrite(...); [не сегодня] - SpinLock; [не сегодня]

  39. Синхронизация. Пользовательский режим: Interlocked-методы Операции типа Compare-And-Swap дают возможность совершить операцию, предполагающую атомарное, потокобезопасное чтение из ячейки памяти и запись в нее.

  40. Синхронизация. Пользовательский режим: Interlocked-методы Под капотом -- это специальные процессорные инструкции. Относительно недороги. Их стоимость зависит от сложности системы кэшей: может отличаться на порядки для компьютеров разных архитектур. Работают быстрее объектов ядра.

  41. Синхронизация. Пользовательский режим: Interlocked-методы (чиним race condition)

  42. Синхронизация. Гибридные конструкции (user-mode/kernel mode) Новые в .Net FW 4 обертки над объектам ядра: ManualResetEventSlim,SemaphoreSlim. Чем они лучше прежних? Еще мы поговорим о: Monitor, ReaderWriterLockSlim, CountdownEvent.

  43. Синхронизация. Гибридные конструкции. Monitor Служит для организации блоков взаимного исключения, обозначения набора операций, которые должны быть выполнены атомарно (с точки зрения других потоков, использующих тот же монитор).

  44. Синхронизация. Гибридные конструкции. Monitor (чиним race condition)

  45. Синхронизация. Гибридные конструкции. Monitor • Потоки, которым не удалось попасть в "критический регион", ждут своей очереди в течение указанного в Monitor.Enter(...) интервала. По умолчанию ждут бесконечно. • Синхронизация кэшей -- другие потоки видят все изменения, но только если используют тот же монитор. • Ожидание: Spin + AutoResetEvent. • Зачем Monitor.Enter и Exit нужен объект -- экземпляр System.Object?

  46. Синхронизация. Гибридные конструкции. Monitor. Ключевое слово lock (пример)

  47. Синхронизация. Гибридные конструкции. Monitor. Хорошо известные косяки • Синхронизация на разных объектах = отсутствие синхронизации. • Синхронизация с монитором на Value-типах. • Синхронизация с монитором на Domain-neutral-объектах (typeof(Int32), интернированных строках, просто строках между доменами и др.). • Синхронизация с монитором по ссылке на текущий объект (this) или ссылке на что-то еще не инкапсулированное (+ MethodImplAttribute). • При использовании нескольких мониторов возможны дедлоки.

  48. Синхронизация. Гибридные конструкции. Monitor. Deadlock

  49. Синхронизация. Гибридные конструкции. ReaderWriterLockSlim Умеет выдавать разделяемые блокировки на чтение, эксклюзивные -- на запись. За счет этого может значительно повысить масштабируемость класса/компонента. - Блокировка на чтение (EnterReadLock...); - Блокировка на запись (EnterWriteLock...); - Upgradeable-блокировка (только 1 поток) -- EnterUpgradeableReadLock; Проблемы Предпочитает писателей (никак не настраивается). Используйте RWLS, если изменения данных происходят намного реже, чем эти данные читаются. Скорость. Не используйте старый ReaderWriterLock:  - он медленнее;  - имеет проблемы с дизайном;  - может приводить к дедлокам;  - отпускает блокировки при апгрейде, нарушая атомарность;

More Related