方法中的 While 循环卡住了。向自身添加字段分配可以解决问题
While loop in a method gets stuck. Adding an assignment of a field to itself fixes the issue
开始我们的 Semaphore 项目时,我给了我的学生一个错误版本的 p() 方法:
proc p() {
while (this.tokens <= 0) {
sleep(1);
writeln("Tokens: ", this.tokens);
}
this.tokens -= 1;
}
我给了他们一些额外的代码来测试它,它会增加另一个线程中的令牌数量(使用 v() 方法)。您可以看到令牌的数量在增加(超过 0),但是代码没有退出 while 循环。
一时兴起,加了一条赋值语句:
proc p() {
while (this.tokens <= 0) {
this.tokens = this.tokens;
sleep(1);
writeln("Tokens: ", this.tokens);
}
this.tokens -= 1;
}
这解决了测试运行中的问题(尽管线程安全性更低)。为什么原来的while循环会卡住,为什么加这个赋值就解决了?
假设 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
,它仍然是 invalid/illegal/undefined 代码,将来很容易被一些 compiler/processor 重新排序所绊倒。
该代码是非法的,因为它违反了 Chapel 的内存一致性模型 (MCM)。具体来说,这是一场数据竞赛,而 Chapel 仅确保无数据竞赛程序的顺序一致性。
内存一致性模型在语言规范中定义(https://chapel-lang.org/docs/1.16/_downloads/chapelLanguageSpec.pdf 中的第 30 章)。它有一个很好且非常易于理解的介绍段落。也就是说,由于它是一种语言规范,因此本章的其余部分非常枯燥且技术性强,因此它可能不是开发人员学习的最佳场所。
如需更简短的概述,请查看 https://chapel-lang.org/CHIUW/2015/hot-topics/01-Ferguson.pdf(尤其是幻灯片 10)。
除此之外,Chapel 的内存模型基于 C11/C++11、Java、UPC 等。如果您查找 "C++11 memory model"、"data race free program" 或 "sequential consistency"
,那里有许多很棒且易于访问的文章
开始我们的 Semaphore 项目时,我给了我的学生一个错误版本的 p() 方法:
proc p() {
while (this.tokens <= 0) {
sleep(1);
writeln("Tokens: ", this.tokens);
}
this.tokens -= 1;
}
我给了他们一些额外的代码来测试它,它会增加另一个线程中的令牌数量(使用 v() 方法)。您可以看到令牌的数量在增加(超过 0),但是代码没有退出 while 循环。
一时兴起,加了一条赋值语句:
proc p() {
while (this.tokens <= 0) {
this.tokens = this.tokens;
sleep(1);
writeln("Tokens: ", this.tokens);
}
this.tokens -= 1;
}
这解决了测试运行中的问题(尽管线程安全性更低)。为什么原来的while循环会卡住,为什么加这个赋值就解决了?
假设 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
,它仍然是 invalid/illegal/undefined 代码,将来很容易被一些 compiler/processor 重新排序所绊倒。
该代码是非法的,因为它违反了 Chapel 的内存一致性模型 (MCM)。具体来说,这是一场数据竞赛,而 Chapel 仅确保无数据竞赛程序的顺序一致性。
内存一致性模型在语言规范中定义(https://chapel-lang.org/docs/1.16/_downloads/chapelLanguageSpec.pdf 中的第 30 章)。它有一个很好且非常易于理解的介绍段落。也就是说,由于它是一种语言规范,因此本章的其余部分非常枯燥且技术性强,因此它可能不是开发人员学习的最佳场所。
如需更简短的概述,请查看 https://chapel-lang.org/CHIUW/2015/hot-topics/01-Ferguson.pdf(尤其是幻灯片 10)。
除此之外,Chapel 的内存模型基于 C11/C++11、Java、UPC 等。如果您查找 "C++11 memory model"、"data race free program" 或 "sequential consistency"
,那里有许多很棒且易于访问的文章