Golangs goroutines提供了阻止I / O到goroutine(-programmer)的接口。在后台运行时,运行时自然会使用某种非阻塞I / O来防止OS挂起OS线程,这样运行时可以在执行I / O时在OS线程上运行另一个goroutine。 / p>
运行时何时考虑执行I / O以便重新安排goroutine?
为了说清楚,假设我有net.TCPConn
我打电话给Write
,我什么时候可以预期重新安排goroutine?
conn, err := net.Dial("tcp", serverAddr)
conn.Write(buffer)
timestamp = time.Now()
那是什么时候可以期待时间戳?
答案 0 :(得分:4)
您可以查看文件https://github.com/golang/go/blob/master/src/net/fd_unix.go(写入功能)。
基本上,它取决于套接字缓冲区是否有足够的空间。
如果套接字缓冲区中有足够的空间来容纳写操作的大小,则数据将立即写入套接字缓冲区。我想这对应于你的第二个答案。此外,内核实际上可以发送数据包(或将其添加到NIC队列),但它独立于Go运行时。
如果套接字缓冲区中没有足够的空间来容纳整个写操作,则只有部分数据会立即写入套接字缓冲区。然后,调用将阻塞(通过运行时轮询引擎),直到内核在套接字缓冲区中产生一些空间(通过发送一些数据包)。只要有一些空间可用,并且所有数据都已复制,呼叫就会解锁。
当网络包通过系统调用将整个缓冲区写入套接字缓冲区时,您应该考虑时间戳。
答案 1 :(得分:2)
following article描述了netpoller的工作原理:
每当goroutine尝试读取或写入连接时,网络代码将执行操作,直到收到此类错误,然后调用netpoller,告诉它在准备好执行I / O时通知goroutine再次。然后将goroutine安排在它正在运行的线程之外,并在其位置运行另一个goroutine。
当netpoller从操作系统收到可以对文件描述符执行I / O的通知时,它将查看其内部数据结构,查看是否有任何goroutine被阻止在该文件上并通知它们是否存在任何。然后goroutine可以重试导致它阻塞并成功执行的I / O操作。“
因此,我们得出结论,只要底层系统调用完成整个缓冲区的写入,就可以重新安排goroutine。在Linux的情况下,当消息被复制到内核空间发送缓冲区时似乎已经过去了:Blocking sockets: when, exactly, does "send()" return?。这又是我的第二个原始选项“当缓冲区被复制到运行时和OS的内核空间时”;也符合Didier Spezia's回答。