Какая разница между сопрограммой и потоком? Есть ли преимущества использования одного над другим?
Какая разница между сопрограммой и потоком? Есть ли преимущества использования одного над другим?
Ответы:
Хотя на первый взгляд сопрограммы работают как потоки, на самом деле они не используют многопоточность. Они выполняются последовательно, пока они yield
. Двигатель проверит все полученные сопрограммы как часть своего основного цикла (в какой момент точно зависит от типа yield
, проверьте эту диаграмму для получения дополнительной информации ), продолжите их один за другим до следующего yield
, а затем продолжите основной цикл.
Преимущество этой техники в том, что вы можете использовать сопрограммы без головной боли, вызванной проблемами с реальной многопоточностью. Вы не получите никаких взаимоблокировок, условий гонки или проблем с производительностью, вызванных переключениями контекста, вы сможете правильно отлаживать и вам не нужно использовать поточно-ориентированные контейнеры данных. Это связано с тем, что при выполнении сопрограммы движок Unity находится в управляемом состоянии. Безопасно использовать большинство функций Unity.
С потоками, с другой стороны, вы абсолютно не знаете, в каком состоянии находится основной цикл Unity в данный момент (фактически он вообще может больше не работать). Таким образом, ваша нить может вызвать немало хаоса, если делать что-то в то время, когда она не должна этого делать. Не трогайте какие-либо собственные функции Unity из подпотока . Если вам необходимо установить связь между подпотоком и вашим основным потоком, попросите поток записать в какой-нибудь потокобезопасный (!) Контейнер-объект и попросите MonoBehaviour прочитать эту информацию во время обычных функций событий Unity.
Недостаток отсутствия «реальной» многопоточности состоит в том, что вы не можете использовать сопрограммы для распараллеливания интенсивных вычислений на нескольких процессорных ядрах. Вы можете использовать их, однако, чтобы разделить расчет на несколько обновлений. Поэтому вместо того, чтобы заморозить игру на одну секунду, вы просто получите более низкую среднюю частоту кадров в течение нескольких секунд. Но в этом случае вы несете ответственность заyield
сопрограммой всякий раз, когда хотите разрешить Unity запускать обновление.
Вывод:
Сопрограммы - это то, что мы в компьютерных науках называем «совместная многозадачность». Они позволяют нескольким различным потокам выполнения взаимодействовать друг с другом. В кооперативной многозадачности один поток исполнения имеет исключительное бесспорное право собственности на CPU , пока он не достигнет yield
. На этом этапе Unity (или любой другой фреймворк, который вы используете) имеет возможность переключаться на другой поток выполнения. Затем он также получает исключительное бесспорное право собственности на CPU до тех пор, yield
пока с.
Потоки - это то, что мы называем «вытесняющая многозадачность». Когда вы используете потоки, фреймворк оставляет за собой право на любой момент остановить ваш поток в середине и переключиться на другой поток. Неважно, где вы находитесь. Вы даже можете быть остановлены на полпути путем записи переменной в память в некоторых случаях!
Есть плюсы и минусы для каждого. Минусы сопрограмм, вероятно, легче всего понять. Во-первых, сопрограммы все выполняются на одном ядре. Если у вас четырехъядерный процессор, сопрограммы будут использовать только одно из четырех ядер. Это упрощает вещи, но может быть проблемой производительности в некоторых случаях. Второй недостаток заключается в том, что вы должны знать, что любая сопрограмма может остановить всю вашу программу, просто отказавшись yield
. Это было проблемой на Mac OS9 много лет назад. OS9 поддерживает только совместную многозадачность на всем компьютере. Если одна из ваших программ зависнет, она может так сильно остановить компьютер, что ОС не сможет даже отобразить текст сообщения об ошибке, чтобы вы знали, что произошло!
Плюсы сопрограмм в том, что их относительно легко понять. Ваши ошибки намного более предсказуемы. Они также обычно требуют меньше ресурсов, которые могут быть полезны, когда вы забираетесь в десятки тысяч сопрограмм или потоков. Кандид Мун упомянул в комментариях, что, если вы не изучили темы должным образом, просто придерживайтесь сопрограмм, и они правы. С сопрограммами работать намного проще.
Нити совершенно разные звери. Вы всегда должны быть настороже против возможности того, что другой поток может прервать вас в любое времяи возиться с вашими данными. Библиотеки потоков предоставляют полный набор мощных инструментов, помогающих вам в этом, таких как мьютексы и условные переменные, которые помогают вам сообщить ОС, когда безопасно запускать один из ваших других потоков и когда это небезопасно. Есть целые курсы, посвященные тому, как правильно использовать эти инструменты. Одна из известных проблем, которая возникает, - это «тупик», когда два потока «застревают», ожидая, пока другой освободит некоторые ресурсы. Другая проблема, которая очень важна для Unity, заключается в том, что многие библиотеки (например, Unity) не предназначены для поддержки вызовов из нескольких потоков. Вы можете очень легко сломать свою платформу, если не будете обращать внимание на то, какие звонки разрешены, а какие запрещены.
Причина такой дополнительной сложности на самом деле очень проста. Модель вытесняющей многозадачности действительно похожа на модель многопоточности, которая позволяет вам не только прерывать другие потоки, но и фактически запускать потоки рядом на разных ядрах. Это невероятно мощный инструмент, который является единственным способом действительно использовать эти новые четырехъядерные и шестнадцатеричные процессоры, которые выходят, но открывают коробку pandoras. Правила синхронизации того, как управлять этими данными в многопоточном мире, явно брутальны. В мире C ++ есть целые статьи, посвященные тому, MEMORY_ORDER_CONSUME
что представляет собой один из немногих, многопоточных способов синхронизации.
Так минусы потоков? Просто: они сложны. Вы можете встретить целые классы ошибок, которых вы никогда не видели раньше. Многие из них являются так называемыми «гейзенгами», которые иногда появляются, а затем исчезают при их отладке. Инструменты, которые вам даны для работы с ними, очень мощные, но они также очень низкого уровня. Они разработаны для того, чтобы быть эффективными в архитектуре современных чипов, а не простыми в использовании.
Однако, если вы хотите использовать всю мощность своего процессора, это инструмент, который вам нужен. Кроме того , есть на самом деле алгоритмы, которые легче понять многопоточности , чем они сопрограмма просто потому , что вы позволяете ручку OS все вопросы о том, где перерывы могут иметь место.
Комментарий Кандид Мун, чтобы придерживаться сопрограмм, также является моей рекомендацией. Если вы действительно хотите силы потоков, то сделайте это. Выйди и действительно изучать темы, формально. У нас было несколько десятилетий, чтобы выяснить, как организовать лучший способ думать о потоках, чтобы вы могли получать безопасные надежные повторяемые результаты на ранних этапах и повышать производительность по мере продвижения. Например, все вменяемые курсы будут обучать мьютексам перед тем, как учить переменные условия. Все вменяемые курсы, которые охватывают атомистику, будут полностью обучать взаимным исключениям и переменным состояниям, даже не упоминая, что атомика существует. (Примечание: не существует такого понятия, как вменяемый учебник по атомной физике.) Попробуйте научиться многопоточности, и вы умоляете о мигрени.
join()
, но вам что-то нужно. Если у вас нет архитектора, который разработал систему для синхронизации, вы должны написать это самостоятельно. Как человек, который занимается многопоточными вещами, я обнаружил, что умственные модели работы компьютеров нужно корректировать, прежде чем они будут выполнять синхронизацию безопасно (именно этому будут учить хорошие курсы)
join
. Моя точка зрения заключалась в том, что использование умеренной доли возможных преимуществ производительности при многопоточности может быть иногда лучше, чем использование более сложного подхода к многопоточности для получения большей доли.
В самых простых сроках возможно ...
Потоки
Поток не решает, когда он выдаст, операционная система («ОС», например, Windows) решает, когда будет получен поток. Операционная система почти полностью отвечает за планирование потоков, она решает, какие потоки запускать, когда их запускать и как долго.
Кроме того, поток может выполняться синхронно (один поток за другим) или асинхронно (разные потоки работают на разных ядрах ЦП). Возможность асинхронного запуска означает, что потоки могут выполнять больше работы за одно и то же время (потому что потоки буквально выполняют две вещи одновременно). Даже синхронные потоки выполняют большую работу, если ОС хорошо их планирует.
Однако эта дополнительная вычислительная мощность сопровождается побочными эффектами. Например, если два потока пытаются получить доступ к одному и тому же ресурсу (например, списку) и каждый поток может быть произвольно остановлен в любой точке кода, изменения второго потока могут мешать изменениям, выполненным первым потоком. (См. Также: Условия гонки и тупик .)
Потоки также считаются «тяжелыми», потому что они имеют много накладных расходов, а это означает, что при переключении потоков происходит значительная потеря времени.
Сопрограммы
В отличие от потоков, сопрограммы являются полностью синхронными, в любой момент времени может быть запущена только одна сопрограмма. Кроме того, сопрограммы могут выбирать, когда давать, и, таким образом, могут выбирать, чтобы давать в точке кода, которая является удобной (например, в конце цикла цикла). Это дает преимущество в таких проблемах, как условия гонки и взаимные блокировки, которых гораздо легче избежать, а также упрощает взаимодействие сопрограмм друг с другом.
Однако это также является основной обязанностью: если сопрограмма не дает должного результата, она может отнять много процессорного времени и все равно может вызвать ошибки, если неправильно модифицирует общие ресурсы.
Сопрограммы, как правило, не требуют переключения контекста, поэтому они быстро включаются и выключаются и довольно легки.
В итоге:
Нить:
сопрограммная:
Роли потоков и сопрограмм очень похожи, но они различаются по тому, как они выполняют свою работу, что означает, что каждый из них лучше подходит для различных задач. Потоки лучше всего подходят для задач, в которых они могут сосредоточиться на выполнении чего-то самостоятельно, не прерываясь, а затем сообщать о завершении. Сопрограммы лучше всего подходят для задач, которые можно выполнить множеством небольших шагов и задач, требующих совместной обработки данных.