Обзор моделей параллельного программирования

Node.js задняя часть Go Язык программирования

Автор: No Dishwashing Studio - Marklux

Источник:Marklux's Pub

Все права принадлежат автору, при перепечатке указывать источник

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

Количество используемых потоков

В «Болях параллелизма» самая трудная проблема параллельного программирования заключается в том, сколько потоков (здесь — потоков ядра) наиболее целесообразно создать. Разные языки по-разному понимают и решают эту проблему, но ее можно грубо классифицировать по соотношению пользовательских потоков к потокам ядра.

Пользовательские потоки: потоки ядра = 1:1

Обратитесь к изображению ниже:

Поток, созданный на языке программирования, полностью отображается на поток ядра, и Java является этой реализацией. Таким образом, планирование потоков будет в основном зависеть от планирования самой операционной системы.Если создается много потоков, производительность может быть низкой из-за проблемы переключения контекста.

Преимущество этого в том, что разработчик напрямую контролирует поток ядра. Благодаря разумному программированию (например, использованию пулов потоков) производительность процессора теоретически может быть увеличена до максимума.

Недостатком является то, что стоимость создания и уничтожения потоков относительно высока, а модель программирования относительно сложна, которую трудно освоить.

Пользовательские потоки: потоки ядра = M:N

То есть пользовательские потоки могут быть сопоставлены с любым потоком ядра. Этот метод является наиболее гибким, но реализация планировщика самого языка программирования будет более сложной, и разработчики больше не смогут напрямую планировать потоки ядра во время разработки. Go должен быть самым производительным языком, реализующим отображение M:N.

Схема отображения Go следующая: M — системный поток, P — планировщик, G — подпрограмма, то есть пользовательский поток.

Когда количество потоков ядра = количеству ядер ЦП, эта модель отображения может легко увеличить использование ЦП программой до высокого уровня.

Преимущество заключается в том, что пользовательские потоки можно сделать очень легкими, а стоимость создания, уничтожения и планирования очень низкой.В большинстве случаев нет необходимости преднамеренно поддерживать пул потоков для повышения производительности, а сложность программирования значительно снижается. .

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

Пользовательские потоки: потоки ядра = N: 1

Как показано ниже

Строго говоря, такое сопоставление не является полным параллелизмом, потому что преимущества производительности нескольких ядер нельзя использовать в полной мере, а задачи нельзя распараллелить.

Он больше используется в скриптовых языках.Сейчас самым известным должен быть JS, да и производительность однопоточной ноды неплохая.Поэтому многие люди сейчас уважают это решение.

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

Методы связи между потоками

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

Заблокировать общие ресурсы

Самое простое решение — использовать несколько потоков для одновременного чтения и записи переменной. Это интуитивно понятно, но неправильное использование блокировки может привести к серьезным потерям производительности, не говоря уже о взаимоблокировках.

Поскольку это очень распространено, я не буду вдаваться в подробности, а основное внимание будет уделено следующим двум способам:

Обмен потоками CSP

Традиционный механизм блокировки не так прост в управлении.В это время появился CSP (Communicating Sequential Process), принцип которого заключается в «совместном использовании памяти посредством связи, а не использовании общей памяти для связи».

Следовательно, режим связи абстрагируется в модели CSP какканал, вся связь между потоками зависит от чтения и записи канала для достижения.

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

Go также добавляет буферное пространство к каналу, так что режим связи канала меняется с одиночной синхронной блокировки на определенную поддержку асинхронности.

Самая большая особенность CSP заключается в том, что он заботится только о способе передачи сообщения и отделяет отправителя от читателя сообщения. Логика этого заключается в том, что «читатель очень обеспокоен тем, обрабатываются ли его данные», если нет. В таком сценарии улучшение, вызванное использованием модели CSP, может быть незначительным.

Модель актера

Эта модель относительно новая. В настоящее время известно, что на Scala существуют относительно масштабные приложения.

В отличие от CSP, Actor фокусируется на блоке обработки сообщения, а не на способе передачи сообщения.И отправитель, и читатель должны знать, кто является другой стороной, прежде чем они смогут отправлять и получать сообщения.

Поскольку модель Актера не использовалась полностью, вот преимущества модели Актера, резюмированные другими:

  • Актеры могут обновляться независимо друг от друга, что позволяет выполнять «горячее» обновление. Поскольку акторы не связаны друг с другом напрямую, они являются относительно независимыми объектами и могут обновляться в горячем режиме.
  • Беспрепятственное соединение локальных и удаленных вызовов Поскольку Актеры используют механизм связи на основе сообщений, независимо от того, взаимодействуют ли они с локальными или удаленными Актерами посредством сообщений, устраняется разница между локальным и удаленным.
  • Связь между отказоустойчивыми субъектами является асинхронной, отправитель просто отправляет и не заботится о тайм-аутах и ​​ошибках, которые берут на себя уровень инфраструктуры и независимые механизмы обработки ошибок.
  • Простое расширение, естественное распространение Поскольку механизм связи актора соединяет локальные и удаленные вызовы, когда локальный актор не может их обработать, вы можете запустить актор на удаленном узле и переслать сообщение.

Связанные ссылки:одновременная боль