Are golang net.UDPConn and net.TCPConn thread safe?? Can i read or write of single UDPConn object in multi thread?

huangapple go评论82阅读模式
英文:

Are golang net.UDPConn and net.TCPConn thread safe?? Can i read or write of single UDPConn object in multi thread?

问题

1.我们可以在同一个net.UDPConn或net.TCPConn对象上从一个线程调用send,从另一个线程调用recv吗?

2.我们可以在同一个net.UDPConn或net.TCPConn对象上从不同的线程并行调用多个send吗?

我找不到关于这个的好文档。
Golang的socket API是线程安全的吗?

我发现很难测试它是否是线程安全的。
对此有任何指导将会很有帮助。

我的测试代码如下:

package main

import (
    "fmt"
    "net"
    "sync"
)

func udp_server() {
    // 创建监听
    conn, err := net.ListenUDP("udp", &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 8080,
    })
    if err != nil {
        fmt.Println("监听失败", err)
        return
    }
    defer conn.Close()

    var wg sync.WaitGroup
    for i := 0; i < 10; i = i + 1 {
        wg.Add(1)
        go func(socket *net.UDPConn) {
            defer wg.Done()
            for {
                // 读取数据
                data := make([]byte, 4096)
                read, remoteAddr, err := socket.ReadFromUDP(data)
                if err != nil {
                    fmt.Println("读取数据失败!", err)
                    continue
                }
                fmt.Println(read, remoteAddr)
                fmt.Printf("%s\n\n", data)

                // 发送数据
                senddata := []byte("hello client!")
                _, err = socket.WriteToUDP(senddata, remoteAddr)
                if err != nil {
                    return
                    fmt.Println("发送数据失败!", err)
                }
            }
        }(conn)
    }
    wg.Wait()
}

func main() {
    udp_server()
}

这个测试代码可以吗?

英文:

1.Can we call send from one thread and recv from another on the same net.UDPConn or net.TCPConn objects?

2.Can we call multiple sends parallely from different threads on the same net.UDPConn or net.TCPConn objects?

I am unable to find a good documentation also for the same.
Is golang socket api thread safe?

I find that it is hard to test if it is thread safe.
Any pointers in the direction will be helpful.

My test code is below:

package main

import (
    &quot;fmt&quot;
    &quot;net&quot;
    &quot;sync&quot;
)

func udp_server() {
    // create listen
    conn, err := net.ListenUDP(&quot;udp&quot;, &amp;net.UDPAddr{
	    IP:   net.IPv4(0, 0, 0, 0),
	    Port: 8080,
    })
    if err != nil {
	    fmt.Println(&quot;listen fail&quot;, err)
	    return
    }
    defer conn.Close()

    var wg sync.WaitGroup
    for i := 0; i &lt; 10; i = i + 1 {
	    wg.Add(1)
	    go func(socket *net.UDPConn) {
		    defer wg.Done()
		    for {
			    // read data
			    data := make([]byte, 4096)
			    read, remoteAddr, err := socket.ReadFromUDP(data)
			    if err != nil {
				    fmt.Println(&quot;read data fail!&quot;, err)
				    continue
			    }
			    fmt.Println(read, remoteAddr)
			    fmt.Printf(&quot;%s\n\n&quot;, data)

			    // send data
			    senddata := []byte(&quot;hello client!&quot;)
			    _, err = socket.WriteToUDP(senddata, remoteAddr)
			    if err != nil {
				    return
				    fmt.Println(&quot;send data fail!&quot;, err)
			    }
		    }
	    }(conn)
    }
    wg.Wait()
}

func main() {
    udp_server()
}

Is it OK for this test code?

答案1

得分: 31

net.Conn的文档中提到:

> 多个goroutine可以同时调用Conn上的方法。

英文:

The documentation for net.Conn says:

> Multiple goroutines may invoke methods on a Conn simultaneously.

答案2

得分: 3

多个 goroutine 可以同时在 Conn 上调用方法。

我对上面的文档的理解是,如果你从多个 goroutine 中调用 ReadWrite 来操作一个 net.Conn,不会发生灾难性的情况,并且对于从多个 goroutine 中的 net.Conn 调用 Write,字节将会被串行化,这样两个不同的 Write 调用的字节在写入网络时不会交错。

你提供的代码的问题在于,Write 并不能保证一次性将整个字节切片写入。你忽略了已写入的字节数。

_, err = socket.WriteToUDP(senddata, remoteAddr)

所以为了确保你写入了所有的数据,你需要循环调用 Write 直到所有的 senddata 都被发送。但是 net.Conn 只能确保来自单个 Write 调用的数据不会交错。考虑到你可能会使用多次写入的方式发送单个数据块,不能保证这个单个数据块会完整地到达目的地。

所以例如,3 个 "hello client!" 消息可能以以下形式到达:

"hellohellohello client! client! client!"

因此,如果你想要在多个 goroutine 中可靠地向 net.Conn 写入消息,你需要同步这些 goroutine,以确保单个消息被完整地写入。

如果我想要做到这一点,作为第一次尝试,我会有一个单独的 goroutine 从一个或多个消息通道读取,并写入一个 net.Conn,然后多个 goroutine 可以向这些消息通道写入。

英文:

> Multiple goroutines may invoke methods on a Conn simultaneously.

My interpretation of the doc above, is that nothing catastrophic will happen if you invoke Read and Write on a net.Conn from multiple go routines, and that calls to Write on a net.Conn from multiple go routines will be serialised so that the bytes from 2 separate calls to Write will not be interleaved as they are written to the network.

The problem with the code you have presented is that there is no guarantee that Write will write the whole byte slice provided to it in one go. You are ignoring the indication of how many bytes have been written.

_, err = socket.WriteToUDP(senddata, remoteAddr)

So to make sure you write everything you would need to loop and call Write till all the senddata is sent. But net.Conn only ensures that data from a single call to Write is not interleaved. Given that you could be sending a single block of data with multiple calls to write there is no guarantee that the single block of data would reach its destination intact.

So for example 3 "hello client!" messages could arrive in the following form.

"hellohellohello client! client! client!"

So if you want reliable message writing on a net.Conn from multiple go routines you will need to synchronise those routines to ensure that single messages are written intact.

If I wanted to do this, as a first attempt I would have a single go routine reading from one or many message channels and writing to a net.Conn and then multiple go routines can write to those message channels.

huangapple
  • 本文由 发表于 2015年2月18日 12:03:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/28575758.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定