C# и io_uring: как подружить асинхронность с ядром Linux
Разбираемся, как заставить C# работать с io_uring без костылей — и почему это быстрее, чем ваш старый async/await.

Помните, как в школе нас учили, что async/await — это просто сахар над ThreadPool? io_uring приходит и говорит: «Держи мое пиво». В первой части цикла мы собрали минимальный io_uring-цикл: mmap, SQE/CQE, accept/recv/send. Теперь самое интересное — соединить это с асинхронной моделью C#.
Мост между мирами
io_uring работает в стиле «подготовил запрос — получил результат». C# async/await — это про «запустил и забыл, пока не понадобится». Как их скрестить? Автор предлагает обертку, которая превращает io_uring-операции в Task. Внутри — классический паттерн с TaskCompletionSource: отправляем SQE, регистрируем колбэк, а при получении CQE вызываем SetResult. Звучит просто, но дьявол в деталях.
Боли и грабли
- Потокобезопасность: io_uring не любит, когда в него лезут из нескольких потоков одновременно. Придется либо использовать один цикл, либо парковать SQE через блокировки.
- Утечки памяти: забыли вызвать Dispose на io_uring-дескрипторе — привет, утечка. А GC про это не знает.
- Совместимость: не все Linux-ядра поддерживают io_uring (нужен 5.1+). На серверах с CentOS 7 придется искать обходные пути.
Стоит ли игра свеч?
Если ваше приложение — высоконагруженный сервер (чат, API-шлюз, прокси), то да. io_uring может дать прирост производительности до 30-50% по сравнению с epoll. Но для типичного веб-приложения на ASP.NET Core — овчинка выделки не стоит. Там и так всё летает.
Комментарий METABYTE: io_uring — отличная штука, но не спешите переписывать весь бекенд. Если у вас legacy на .NET Framework, лучше сначала подумать о миграции на современный стек. А мы поможем с архитектурой, чтобы не пришлось переписывать дважды.
СЛЕДУЮЩИЙ ШАГ
Понравилось как мыслим?
Применяем те же принципы в клиентских проектах: AI, автоматизации, продукты, которые не умирают после релиза.