Простейший способ переключения контекста взад и вперед (диспетчеризация)

#multithreading #design-patterns #grand-central-dispatch #swift5

#многопоточность #шаблоны проектирования #grand-central-диспетчеризация #swift5

Вопрос:

У меня есть два класса, которые выполняют независимые вычисления. Для простоты здесь я буду представлять их с помощью функций calc1 и calc2 . В некоторых случайных местах в каждой функции мне нужно некоторое время поспать. Вместо того, чтобы выполнять calc1 and then calc2 , я бы хотел переключаться между ними взад и вперед.

Я мог бы использовать потоки, но на первый взгляд мне кажется, что это слишком специально для этого. Кроме того, эти две функции должны быть в главном потоке, поскольку они имеют дело с пользовательским интерфейсом. Мне не нужны разные потоки. Мне просто нужно переключиться с одного контекста на другой, и вернуться туда, где мы были.

В Python существует концепция greenlet ( gevent ), которая позволяет просто переключать контекст, не являясь реальным потоком. Это было бы идеально для моих нужд. Есть ли такой механизм в swift ?

 func calc1() {
...
sleep(300) // go to calc2
...
}

func calc2() {
...
sleep(200) // resume calc1
...
}
  

Комментарии:

1. Вы должны использовать потоки. Если вы переводите поток в спящий режим, по определению, никакой код не будет выполняться в этом потоке, пока он находится в спящем режиме. Единственный способ выполнить какой-либо другой код — запустить его в другом потоке. QED

2. Я надеялся, что будет какой-то способ заменить режим ожидания чем-то, что вызывает grand central dispatcher и просто переходит к выполнению кода в другом контексте

3. Да, есть, но я понятия не имею, чего вы пытаетесь достичь. dispatch_sync Приостановил бы (спящий режим) первый поток, в то время как второй выполняет блок в другом потоке… но с какой целью и чем это отличается от простого вызова другой функции? Вам действительно нужно описать, чего вы пытаетесь достичь; без этого контекста это просто общие ответы на неопределенные вопросы.

4. К сожалению, этот вопрос слишком расплывчатый, чтобы мы могли дать вам какой-либо осмысленный ответ. Существует слишком много альтернатив, зависящих от деталей того, что вы делаете. Я бы посоветовал вам дать нам представление о видах вычислений, которые вы выполняете. Но просто очень непонятно, почему вы не хотели бы, чтобы они запускались одновременно, учитывая, что GCD делает это настолько простым, и практически все устройства в настоящее время имеют несколько ядер. Переключение контекста может оказаться гораздо более сложным, чем если бы вы просто выполняли его параллельно.

5. Хорошо, я попытался немного подробнее объяснить, что делают эти функции, и почему я не могу использовать потоки. Возможно, ответ заключается в том, что нет способа легко делать то, что я хочу, и это нормально…

Ответ №1:

Эта идея альтернативного переключения между двумя дорогостоящими вычислениями в главном потоке не сработает.

  • Во-первых, мы никогда не делаем ничего вычислительно дорогостоящего в основном потоке. Мы никогда не должны блокировать основной поток по какой-либо причине. Это приводит к ужасному UX (может показаться, что ваше приложение заморожено), и вы рискуете, что приложение будет уничтожено процессом watchdog ОС (который ищет приложения, которые кажутся замороженными и блокируют основной поток).

  • Во-вторых, при вычислении двух действительно независимых вычислений мы бы не добавляли накладных расходов и сложности при попытке переключаться между ними. Мы бы просто использовали GCD для независимой отправки их в фоновые очереди.

Итак, решением для такого рода процессов было бы отправить эти две задачи для одновременного выполнения в фоновых очередях, либо в одну параллельную очередь, либо в две выделенные очереди. Но ключ в том, чтобы выполнять сложные вычисления вне основного потока параллельно, а затем отправлять периодические обновления пользовательского интерфейса обратно в основной поток. Но всегда оставляйте основной поток свободным для реагирования на вводимые пользователем данные, системные события и т.д.

Черт возьми, этот процесс диспетчеризации этих двух независимых задач по отдельности проще и обеспечивает гораздо более эффективное использование ресурсов устройства. Просто синхронизируйте обновления с объектами модели. И группы отправки — отличный способ отслеживать, когда завершаются две независимые параллельные задачи.