在学习golang的过程中,当我试图理解内存模型规范中描述的通道通信时,我有点困惑,如下所示:
- 通道上的发送在该通道的相应接收完成之前发生.
- 关闭通道发生在返回零值的接收之前,因为通道已关闭.
- 来自无缓冲通道的接收在该通道上的发送完成之前发生.
- 具有容量C的信道上的第k个接收发生在从该信道发送的第k + Cth个完成之前.
前两条规则清晰且易于理解,而我对第三条规则感到困惑,这似乎与其他规则相反......我是否错过了任何关于无缓冲通道的特殊规定?或者我是否正确如果我像下面的规范中的示例那样:
var c = make(chan int)
var a string
func f() {
a = "hello, world"
<-c // A
}
func main() {
go f()
c <- 0 // B
print(a)
}
Run Code Online (Sandbox Code Playgroud)
对于无缓冲通道,发送操作(B)被阻塞,直到接收器准备好接收值(A)?(比如:B开始并且直到A执行才返回)它准确吗?
我在Effective Go规范中找到了以下陈述,但是我的理解仍然存在差异......那么有人可以用简单直接的方式解释这个吗?
接收器始终阻塞,直到有数据要接收.如果通道未缓冲,则发送方将阻塞,直到接收方收到该值.如果通道有缓冲区,则发送方仅阻塞,直到将值复制到缓冲区为止; 如果缓冲区已满,则表示等待某个接收方检索到某个值.
现在 stackoverflow 上已经有很好的答案,但他们没有给我想要的明确答案。
\n\n说你有方法
\n\nDosomething();\n doAnother();\n int x = 5;\n Runnable r = new Runnable(){\n public void run(){\n int y = x;\n x = 7;\n System.out.println(z);}\n }\n new Thread(r).start()\nRun Code Online (Sandbox Code Playgroud)\n\n现在,同时该方法正在运行,并且在调用 thread.start 之前,一些全局非易失性变量 z 从 4 更改为 5。
\n\n由于 z 发生在 thread.start 之前,程序是否能保证打印 5?
\n\n另外,如果我们以某种方式谈论它,那么可以肯定地说 thread.start() 永远不能重新排序。
\n\n就被称为启动的线程而言,这意味着,就好像到该点为止的所有内容都是顺序的。例如说我们有
\n\nint k = 8;\nnew Thread(() -> {}).start()\nRun Code Online (Sandbox Code Playgroud)\n\n现在...从该线程的角度来看,无论首先调用 start 还是 k 被分配 8,都不会产生任何影响。因此可以重新排序,但由于发生在保证之前,这不可能吗?
\n\njava 规范并没有给出强有力的声明来说明这一点。相反它说
\n\n当语句调用\xc2\xa0Thread.start()时,与该语句有happens-before关系的每个语句
\n\n然而 k = 8 并不表示 a …
我有一个非常特别的问题,我找不到答案。
正如我们所知,在synchronized块的入口处,线程会重新读取其范围内的所有共享(非本地)变量。某些底层架构的示例:如果线程 A 更新 RAM 中的对象状态,线程 B 进入同步块将看到更改。类似的事情发生在退出synchronized块时:线程将其范围内的所有内容刷新到 RAM,以便其他线程可以看到它。这些是基本的 JVM 可见性保证,并且存在发生之前的规则来强制执行它。
但是,从语义上讲,代码使用wait()或notify()也执行所有这些操作并不是很清楚:毕竟它没有明确地进入或离开synchronized块。
这些问题是:
wait()入口处对其他线程的更改可见?wait()休假时所做更改的可见性?notify()?java multithreading synchronized java-memory-model happens-before
考虑以下代码。
public class Test {
private boolean running = false;
public void run() {
running = true;
}
public void test() {
boolean running1 = running;
boolean running2 = running;
System.out.println("running1: " + running1);
System.out.println("running2: " + running2);
}
}
Run Code Online (Sandbox Code Playgroud)
线程 A 调用run(),然后另一个线程 B 调用test()并且不应该有任何发生之前的关系。我知道不能保证线程 B 看到线程 A 所做的更改。但是这个程序的输出有没有可能是:
running1: true
running2: false
Run Code Online (Sandbox Code Playgroud) java concurrency multithreading thread-safety happens-before
我知道,有两种方法可以在java中建立before-before关系:synchronized块和方法,volatile关键字.(如果我是正确的,它不适用于最终字段).我的问题是:并发包中的原子变量是否相似?可以发生 - 之前由他们建立?
java concurrency multithreading java.util.concurrent happens-before
最近,我发现一些代码如下所示:
var m map[int]int
func writem() {
tmpm := make(map[int]int)
for i := 0; i < 4000000; i++ {
tmpm[i] = i + 10
}
m = tmpm
}
func readm() {
for k, v := range m {
_, _ = k, v
}
}
func main() {
writem()
go readm()
writem()
}
Run Code Online (Sandbox Code Playgroud)
该程序运行良好,但是我认为writem可以通过m = tmpm在for循环之前移动功能主体来重新排序,因为这不会更改此goroutine中的行为。而且这种重新排序将导致concurrent map read and map write问题。正如Go Memory Model所说:
仅当重新排序不会改变语言规范所定义的该goroutine中的行为时,编译器和处理器才可以对单个goroutine中执行的读写进行重新排序。
是的,还是这样编写代码安全吗?
concurrency ×4
java ×4
go ×2
channel ×1
data-race ×1
memory-model ×1
synchronized ×1
visibility ×1