我的节目是这样的:
func handle(conn net.Conn) {
msg := "hello, world!"
for i:= 0; i< 100000; i++ {
go func() {
err := write(conn, msg)
}
}
}
func write(conn net.Conn, msg string) error {
mlen := fmt.Sprintf("%04d", len(msg))
_, err := conn.Write([]byte(mlen + msg))
return err
}
该程序将同时运行100000个goroutine,所有goroutine将向同一连接发送消息。 我怀疑服务器是否会收到错误消息,例如&#34; hellohelloworldworld&#34;,但是当程序在我的Ubuntu 14.04LTS中运行时没有问题。
那么,多个goroutine会同时在Conn上调用一个方法吗?
=============================================== ==========================
如何保持Write
方法的原子性?
答案 0 :(得分:4)
文档说明:
多个goroutine可以同时调用Conn上的方法。
没有提到每个单独的写是否是原子的。虽然当前的实现可以确保在下一次调用开始之前完成对Write
的每次调用,但是在语言规范中无法保证。
答案 1 :(得分:0)
此 answer 表示写入是原子的。
io.Write 接口的具体实现者需要在发生部分写入时返回错误。 net.Conn 在 unix 上通过获取锁并在循环中调用 write 直到整个缓冲区被写入来处理这个问题。在 Windows 上,它调用 WSASend 保证发送整个缓冲区,除非发生错误。但是文档have this warning:
<块引用>调用 WSASend 的顺序也是调用 WSASend 的顺序 缓冲区被传输到传输层。 WSASend 不应该是 从不同的并发调用同一个面向流的套接字 线程,因为某些 Winsock 提供程序可能会拆分大型发送请求 多次传输,这可能会导致意外数据 从多个并发发送请求交错 面向流的套接字。
这意味着它不一定是原子的,除非 Go 获得互斥锁 - which it does。
所以基本上它在实践中是原子的。可以想象,一个实现可以将线程安全定义为不崩溃,并通过解锁写调用周围的互斥锁(或在 Windows 上根本不获取它)来允许交错写入。但这对我来说没有意义,而且开发人员已经清楚地表明了相反的意图。