Почему не рекомендуется использовать unconfined в корутинах

Ответ

Unconfined — это диспетчер, который запускает корутину в текущем потоке до первой точки приостановки, а затем продолжает её в потоке, который возобновляет работу.

Проблемы:

  1. Непредсказуемость потока — после suspend корутина может продолжиться в другом потоке, что усложняет отладку.
  2. Риск блокировки UI — если начать в главном потоке и вызвать блокирующую операцию до первой точки приостановки, UI зависнет.
  3. Не рекомендуется для прода — подходит только для тестов или очень специфичных случаев.

Пример:

// Плохо: может блокировать UI или переключиться на другой поток
GlobalScope.launch(Dispatchers.Unconfined) {
    doBlockingWork() // Опасно, если вызвано в Main-потоке!
    withContext(Dispatchers.IO) { ... } // После suspend поток может измениться
}

Лучше использовать явные диспетчеры (Dispatchers.IO, Dispatchers.Default или Dispatchers.Main).

Ответ 18+ 🔞

Давай разберём этот Unconfined, а то звучит как какой-то хипстерский йогурт, а на деле — просто пиздец какой-то.

Смотри, представь себе диспетчер. Обычный диспетчер — это как строгий прораб: «Ты, корутина, станешь вон там, в потоке для ввода-вывода, и будешь там работать, пока не закончишь». А Unconfined — это прораб-анархист, ёпта. Он такой: «О, чувак, начинай работать прямо здесь, где стоишь! А дальше... как карта ляжет, хрен его знает».

В чём, собственно, засада:

  1. Непредсказуемость, пиздец. Ты запускаешь корутину, допустим, в главном потоке. Она делает что-то синхронное, ок. Но как только встречает первую точку приостановки (suspend), она как бы говорит: «Всё, я тут закончила». А когда её возобновляют, она может продолжить выполнение в абсолютно любом другом потоке, который её вызвал. Это ж ебать-копать, отладка превращается в ад. Сегодня работает, завтра — нихуя. Потому что поток сменился.

  2. Риск угробить UI. Вот классика: ты на Android в главном потоке запускаешь с Unconfined и до первого suspend делаешь тяжёлую блокирующую операцию (типа файл прочитать). Ну и всё, приложение повисло, как будто на дворе 2002-й год. Пользователь тебе мордой об экран тапнет. Зачем так жить?

  3. Для прода — ни-ни. Серьёзно, это как брать на стройку хлипкую стремянку «только для покраски забора». В 99.9% случаев она тебе нахуй не сдалась. Используй её только если ты пишешь тест, или у тебя такой специфичный, ебанутый кейс, что другого выхода нет. А так — доверия к этому диспетчеру ноль ебать.

Смотри, как не надо:

// ПЛОХО, НЕ ДЕЛАЙ ТАК, А ТО ПРИДЁТ ЧЁРТ И ЗАБЕРЁТ ТВОЙ КОД
GlobalScope.launch(Dispatchers.Unconfined) {
    doBlockingWork() // Опасно! Если это Main-поток, то UI накрылся медным тазом.
    withContext(Dispatchers.IO) { ... } // А после этого 'suspend' ты уже можешь очутиться в другом потоке. Веселье!
}

Вместо этой манды с ушами просто возьми нормальный, предсказуемый диспетчер: Dispatchers.IO для работы с файлами/сетью, Dispatchers.Default для вычислений, Dispatchers.Main для обновления интерфейса. Не выёбывайся.

Короче, Unconfined — это как дать обезьяне гранату. Интересно посмотреть, но жить с этим потом овердохуища проблем.