TaskScheduler - пул потоков для task-ов
SynchronizationContext - содержит свой поток и данные, который не используется TaskScheduler-ом
Task.Run - достаёт свободный поток из TaskScheduler.
await Task.Run - запоминает текущий SynchronizationContext (если есть), останавливает текущую задачу, запускает другую задачу.
Когда дочерняя задача завершится, наша задача продолжит выполнение в:
- потоке из запомненного контекста синхронизации
- потоке из TaskScheduler.
Т.е. async/await нужен для возврата потока выполнения к контексту синхронизации, если он есть. И не использовать всякие BeginInvoke