Предполагая, что p()
и v()
вызываются из разных задач, нет никакой гарантии, что запись в неатомарную this.tokens
в одной задаче когда-либо будет видна другой задаче.
Одним из решений было бы сделать tokens
атомарным и иметь что-то вроде:
proc p() {
while (this.tokens.read() <= 0) {
sleep(1);
writeln("Tokens: ", this.tokens.read());
}
this.tokens.sub(1);
}
Когда tokens
не является атомарным, компилятор может предположить, что tokens
не будет изменен другими задачами, поэтому он, вероятно, преобразует ваш код во что-то вроде:
var tokenTemp = this.token;
while (tokenTemp <= 0)
...
и вставка записи в token
предотвращает этот подъем. Тем не менее, даже с записью в token
, это все еще недопустимый/незаконный/неопределенный код и может быть легко отключен каким-либо переупорядочением компилятора/процессора в будущем.
Этот код является незаконным, поскольку он нарушает модель согласованности памяти Chapel (MCM). В частности, это гонка данных, и Chapel обеспечивает последовательную согласованность только для программ, свободных от гонок данных.
Модель согласованности памяти определена в спецификации языка (глава 30 в https://chapel-lang.org/docs/1.16/_downloads/chapelLanguageSpec.pdf). У него хороший и довольно доступный вводный абзац. Тем не менее, поскольку это спецификация языка, остальная часть главы довольно сухая и техническая, так что это, вероятно, не лучшее место для обучения разработчиков.
Более краткий обзор см. на https://chapel-lang.org/CHIUW/2015/hot-topics/01-Ferguson.pdf (особенно слайд 10).
Кроме того, модель памяти Chapel основана на C11/C++11, Java, UPC и других. Существует множество отличных и доступных статей, если вы ищете «модель памяти C++ 11», «программу без гонки данных» или «последовательную согласованность».
person
Elliot
schedule
09.02.2018