Автор: 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 фокусируется на блоке обработки сообщения, а не на способе передачи сообщения.И отправитель, и читатель должны знать, кто является другой стороной, прежде чем они смогут отправлять и получать сообщения.
Поскольку модель Актера не использовалась полностью, вот преимущества модели Актера, резюмированные другими:
- Актеры могут обновляться независимо друг от друга, что позволяет выполнять «горячее» обновление. Поскольку акторы не связаны друг с другом напрямую, они являются относительно независимыми объектами и могут обновляться в горячем режиме.
- Беспрепятственное соединение локальных и удаленных вызовов Поскольку Актеры используют механизм связи на основе сообщений, независимо от того, взаимодействуют ли они с локальными или удаленными Актерами посредством сообщений, устраняется разница между локальным и удаленным.
- Связь между отказоустойчивыми субъектами является асинхронной, отправитель просто отправляет и не заботится о тайм-аутах и ошибках, которые берут на себя уровень инфраструктуры и независимые механизмы обработки ошибок.
- Простое расширение, естественное распространение Поскольку механизм связи актора соединяет локальные и удаленные вызовы, когда локальный актор не может их обработать, вы можете запустить актор на удаленном узле и переслать сообщение.
Связанные ссылки:одновременная боль