Объясните: не общайтесь, обмениваясь памятью; делитесь памятью, общаясь
интересно, что самое простое объяснение этой знаменитой цитаты:
Не общайтесь, обмениваясь памятью; делитесь памятью, общаясь. (Р. Пайк)
на Модель Памяти Go Я могу читать это:
отправка по каналу происходит до завершения соответствующего приема от этого канала. (Golang Spec)
существует также специальная статьи golang объясняя цитату. И ключевым вкладом является пример работающего также Эндрю г.
хорошо. Иногда слишком много болтают .... Я получил из цитаты спецификации памяти, а также, посмотрев на рабочий пример, это:
после того, как goroutine1 отправляет (что-нибудь) в goroutine2 по каналу, тогда все изменения (в любом месте памяти), сделанные goroutine1, должны быть видны goroutine2 после его получения по тому же каналу. (Golang Лемма по Я:)
поэтому я получаю приземленное объяснение знаменитой цитаты:
чтобы синхронизировать доступ к памяти между двумя goroutines, вам не нужно отправлять эту память по каналу. Достаточно хорошо получать от канала (даже ничего). Вы увидите любые изменения, которые были написаны (в любом месте) отправкой goroutine (на канал) во время его отправки. (Конечно, при условии, что никакие другие горутины пишем одну и ту же память.) "обновить" (2) 8-26-2017
У меня на самом деле два вопроса:
1) Правильно ли мое заключение?
2) помогает ли мое объяснение?
обновление (1) Я предполагаю unbuffered channels. Давайте сначала ограничимся этим, чтобы не перегружать себя слишком большим количеством неизвестных.
пожалуйста, давайте также сосредоточимся на простом использовании двух goroutines общение по одному каналу и связанные с ним эффекты памяти, а не на лучшие практики - это выходит за рамки этого вопроса.
чтобы лучше понять область моего вопроса, предположим, что goroutines могут получить доступ к любой структуре памяти - не только к примитивным - и она может быть большой, это может быть строка, карта, массив, что угодно.
6 ответов
по сути, да. Любые значения, назначенные переменным до отправки канала, могут наблюдаться после чтения канала, так как операция канала накладывает ограничение упорядочения. Но важно помнить другую часть уравнения: если вы хотите гарантия что эти значения наблюдаются, вы должны убедиться, что никто больше не может писать в эти переменные между записью и чтением. Очевидно, использование замков было бы возможно, но бессмысленно в то же время, если вы уже комбинируете блокировки и модификацию памяти с перекрестными потоками, какую пользу вы получаете от каналов? Вы могли бы передать что-то простое, как логическое, как токен, позволяющий эксклюзивный доступ к глобальным данным, и это было бы 100% правильно с точки зрения гарантий модели памяти (пока ваш код не имел ошибок),Это, вероятно, будет плохой дизайн потому что вы бы делали вещи все неявными и действие-на-расстоянии-y без веской причины; передача данных явно обычно будет более четкой и менее подверженной ошибкам.
эта знаменитая цитата может быть немного запутанной,если ее слишком много. Давайте разберем его на более основные компоненты и определим их правильно:
Don't communicate by sharing memory; share memory by communicating
---- 1 ---- ------ 2 ----- ---- 3 ----- ----- 4 -----
- это означает, что различные потоки выполнения будут проинформированы об изменении состояния других потоков путем чтения памяти, которая будет изменена где-то еще. Прекрасным примером этого (хотя для процессов вместо потоков) является API общей памяти POSIX: http://man7.org/linux/man-pages/man7/shm_overview.7.html. Этот метод требует правильной синхронизации, потому что гонки данных могут происходить довольно легко.
- здесь это означает, что действительно существует часть памяти, физическая или виртуальная, которая может быть изменена из нескольких потоков и прочитана из них. Нет явного понятия владения, пространство памяти одинаково доступно из всех потоков.
- это совсем другое. В Go, обмен памятью просто как и выше, возможно, и гонки данных могут происходить довольно легко, так что это на самом деле означает, что изменение переменной в goroutine, будь то простое значение, такое как
int
или сложная структура данных, такая как карта, и отдает право собственности, отправляя значение или указатель на значение другой goroutine через механизм канала. Поэтому в идеале нет общего пространства, каждая горутина видит только ту часть памяти, которой она владеет. - связь здесь просто означает, что канал, это всего лишь очередь, позволяющая одной горотине читать из нее и таким образом получать уведомление о владении новой частью памяти, в то время как другая отправляет ее и принимает, чтобы потерять владение. Это простой шаблон передачи сообщений.
В заключение, что означает цитата, можно суммировать следующим образом:
Не перегружайте взаимодействие между потоками с помощью общей памяти и сложных примитивов синхронизации, подверженных ошибкам, но вместо этого используйте передачу сообщений между goroutines (зеленые потоки), поэтому переменные и данные могут использоваться в последовательности между ними.
использование последовательности слов здесь примечательно, потому что оно описывает философию, которая вдохновила концепцию goroutines и каналов:Передача Последовательных Процессов.
Я так не думаю. Суть в том, что вместо защиты одного фиксированного адреса памяти с помощью блокировки или других параллельных примитивов вы можете создать программу таким образом, чтобы доступ к этой памяти был разрешен только одному потоку выполнения конструкция.
простой способ достичь этого-поделиться ссылкой на память по каналам. Как только вы отправите ссылку на канал забыть. Таким образом, только рутина, которая будет потреблять это канал будет иметь к нему доступ.
в этом есть два предложения; для более полного понимания их нужно сначала посмотреть отдельно, а затем собрать вместе, поэтому:
Don't communicate by sharing memory;
середины, различные потоки не должны связывать друг с другом путем исполнять к строгим и ошибк-прональным политикам видимости и синхронизации памяти как барьер памяти ЕТК. (Обратите внимание, что это можно сделать, но вскоре это может стать сложным и очень багги с гонками данных). Поэтому избегайте соответствия ручным, программным конструкциям видимости в основном достигается через правильную синхронизацию в языках программирования, таких как Java.
share memory by communicating.
означает, что если один поток внес какие-либо изменения (записи) в область памяти, он должен передавать то же самое (область памяти) потоку, заинтересованному в той же области памяти; обратите внимание, что это уже ограничило область памяти только двумя потоками.
теперь прочитайте выше два абзаца в сочетании с моделью памяти golang, которая гласит:A send on a channel happens before the corresponding receive from that channel completes.
отношение случается-прежде чем подтверждает что писать на первой горутина будет-будет видно второй горутина получения ссылки памяти на другом конце канала!
отпусти меня и здесь.
Не общайтесь путем обмена памятью.
Это похоже на то, когда вы общаетесь с помощью потоков, например, вы должны использовать переменные или мьютексы для блокировки памяти, чтобы не позволять кому-то читать и писать на нем, пока связь не будет завершена.
поделиться памятью, общаясь
в подпрограммах go значения перемещаются по каналам, а не блокируют память, отправитель уведомляет приемник, чтобы получить от этого канала, и, следовательно, он разделяет память, общаясь с приемником, чтобы получить от канала.
1) Правильно ли мое заключение?
Я так думаю, если это означает, что я надеюсь на это. Причина, по которой язык в спецификациях использует терминологию "случайности", заключается в том, что он обеспечивает четко определенную форму коммуникации для выражения идей.
проблема с вашим описанием заключается в том, что вы на самом деле явно не определили свой порядок случайности. Я думаю, что вы подразумеваете приказ. Если вы имеете в виду, что " операции в goroutine a, что произойдет до того, как goroutine a будет работать на определенной точке синхронизации, будет виден goroutine b после того, как goroutine b также заметил, что та же самая точка синхронизации" - даже здесь, хотя "точка синхронизации" плохо определена - хотя я ожидаю, что вы это понимаете. Такая точка может быть определена случайно, как это делает спецификация.
2) помогает ли мое объяснение?
может быть, люди, незнакомые с темой или борьба с пониманием стиля описания случайности может найти ваше описание легче интерпретировать. Однако существуют ограничения и потенциальные практические проблемы в приложении вашего описания, а именно:
- вы не строго определили, что "отправить", о котором вы говорите, является точкой синхронизации. Если вы имеете в виду отправку по небуферизованному каналу, то да, это создаст общую точку синхронизации, которая по спецификации вводит строгий порядок случайности.
- хотя, предполагая, что это верно, вы описали точку синхронизации, это касается только одной стороны точки исходного совета. Оригинальный совет включает в себя концепцию "передачи права собственности", и это имеет меньшее отношение к созданию точек синхронизации или случайности, и больше связано с более долгосрочным обслуживанием кода, который полагается на потенциально общую память. Концепция заключается в том, что вместо сохранения доступа к некоторому сегменту память в двух местах и создание отдельных общих точек синхронизации (например, мьютексов) вместо этого можно передать, возможно, единственную ссылку на объект от одного владельца к другому. Разработка программного обеспечения таким образом предотвращает случайную модификацию вне точек синхронизации, что часто наблюдается в программном обеспечении с гранулированным использованием мьютексов и широким использованием общей памяти.
мьютексы или "явные точки синхронизации" точно противоположны совет - они являются общим фрагментом памяти, который используется для связи точки синхронизации "общаться путем обмена памятью", в то время как, хотя они имеют мьютексы глубоко под капотом, каналы являются абстрактным механизмом передачи права собственности на объект (отправленное значение) от одного goroutine (отправитель) к другому goroutine (получатель). Дело в том, что, игнорируя то, как реализуются каналы, пользователь имеет общую память (значение), передавая ее от одного владельца (goroutine a) другому (goroutine b). Если вы используете канал для отправки несвязанных данных для создания точки синхронизации, вы по существу используете его как мьютекс, и это ближе к общению путем совместного использования памяти (фокус на канале), а не совместного использования путем общения (фокус на значении).
надеюсь, это поможет.