Большинство проблем с многопоточностью выглядят одинаково везде. Но часть из них появляется только в конкретной среде. WinForms, WPF и ASP.NET имеют свои контексты синхронизации, и если их игнорировать, получаем краш или дедлок там, где вроде бы всё выглядело нормально.
Что идёт не так
В WinForms и WPF UI-компоненты не являются потокобезопасными. Обновлять их можно только из UI-потока. Если фоновый поток попытается напрямую записать что-то в
label.Text или textBox.Value, получаем InvalidOperationException. В WPF для этого используется Dispatcher.BeginInvoke, в WinForms — Control.Invoke или Control.BeginInvoke.Казалось бы, очевидное правило. Но баг всё равно появляется: чаще всего тогда, когда разработчик делает
Task.Run, внутри него обращается к UI, а анализатор это не видит.С
Dispatcher.Invoke другая история. Это синхронный вызов, он блокирует текущий поток до завершения. Если вызвать Invoke из самого UI-потока или из кода, который UI-поток уже ждёт, получаем дедлок. Правило простое: почти всегда нужен BeginInvoke (асинхронный), а не Invoke.Как находить такие баги до прода
Статические анализаторы:
Roslyn, AsyncFixer, Microsoft.VisualStudio.Threading.Analyzers и ThreadSafetyAnalyzer умеют находить прямые обращения к UI из неправильного потока, синхронные блокировки async-методов и потенциальные дедлоки в диспетчере.Подключить их можно через NuGet:
dotnet add package Microsoft.VisualStudio.Threading.Analyzers
dotnet add package AsyncFixer
После подключения анализаторы начинают предупреждать прямо в IDE, до сборки. Часть правил даже предлагает автофикс.
На практике это работает: после включения анализаторов в одном проекте удалось найти три гонки данных ещё до того, как они добрались до пользователей.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека шарписта
#sharp_view