并发性 | goroutines | golang | 缓冲读取器

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

concurrency | goroutines | golang | buffered reader

问题

相信我要么误解了go routines的工作原理,要么误解了缓冲读取器的工作原理,或者两者都误解了。

期望的是goroutine的异步执行(使用带有for循环的缓冲读取器从缓冲区读取消息,等待服务器的消息)。

尝试使用方法A在客户端拨号服务器之前调用go xyz(),这样xyz()会创建缓冲区并在后台开始读取。然后,客户端拨号服务器;服务器发送消息回来;客户端正在读取缓冲区,所以它获取消息并打印到控制台。

实际发生的是客户端向服务器发送消息,但在等待服务器可能的回复时没有从缓冲区中获取任何内容;所以它是并发运行的,因为我知道for循环没有停止,但它让下一行代码执行(客户端向服务器发送消息)。

但是,当我使用方法B在客户端拨号服务器之后调用xyz()时,一切都按预期工作。客户端从服务器获取消息并打印到控制台。

方法A的顺序如下:

///////////// 步骤1和2在由go xyz()调用的goroutine中

  1. 创建缓冲读取器

  2. for循环--从服务器读取缓冲区的消息--打印出来

  3. 客户端拨号服务器

    go xyz(conn, p)

    fmt.Fprintf(conn, "Give me a hash to work on ...")

方法B的顺序如下:

///////////// 步骤2和3在由xyz()调用的goroutine中

  1. 客户端拨号服务器

  2. 创建缓冲读取器

  3. for循环--从服务器读取缓冲区的消息--打印出来

    fmt.Fprintf(conn, "Give me a hash to work on ...")

    xyz(conn, p)

client.go

package main
import (
    "fmt"
    "net"
    "bufio"
)

func xyz(conn net.Conn, p []byte) {
  rd := bufio.NewReader(conn)
  for {
    _, err := rd.Read(p)
    if err == nil {
      fmt.Printf("SERVER : %s\n", p)
    } else {
        fmt.Printf("Some error %v\n", err)
    }
  }
}

func main() {
    p :=  make([]byte, 2048)
    conn, err := net.Dial("udp", "127.0.0.1:1234")
    if err != nil {
        fmt.Printf("Some error %v", err)
        return
    }

    go xyz(conn, p)
    fmt.Fprintf(conn, "Give me a hash to work on ...")
}

server.go

package main
import (
    "fmt"
    "net"
)


func sendResponse(conn *net.UDPConn, addr *net.UDPAddr, hash string) {
    _,err := conn.WriteToUDP([]byte("Hello, here is the hash  - " + hash), addr)
    if err != nil {
        fmt.Printf("Couldn't send response %v", err)
    }
}


func main() {
    hash := "36";
    p := make([]byte, 2048)
    addr := net.UDPAddr{
        Port: 1234,
        IP: net.ParseIP("127.0.0.1"),
    }
    ser, err := net.ListenUDP("udp", &addr)
    if err != nil {
        fmt.Printf("Some error %v\n", err)
        return
    }
    for {
        _, remoteaddr, err := ser.ReadFromUDP(p)
        fmt.Printf("CLIENT : %v : %s\n", remoteaddr, p)
        if err !=  nil {
            fmt.Printf("Some error  %v", err)
            continue
        }
        go sendResponse(ser, remoteaddr, hash)
    }
}
英文:

Believe I am either misunderstanding how go routines work, how buffered readers work or both.

Expect an asynchronous execution of the goroutine ( a buffered reader with a for loop reading the buffer, waiting for a message from the server )

Try METHOD A to call go xyz() before the client dials the server; so xyz() creates the buffer and starts reading in the background. Then, the client dials the server; server sends message back; the client is reading the buffer so, it gets the message and prints to console

What Actually Happens the client to send the message to the server, but does not pick up anything on the buffer while reading for possible reply from server; so it is running concurrently because I know the for loop has not stopped, but it lets the next line of code execute ( client sending message to server ).

But When METHOD B I call xyz() NOT concurrently and after the client dials the server, all things work as expected. The client gets the message back from the server and prints to console.


METHOD A, we have the order :

///////////// steps 1 and 2 are in the goroutine called by go xyz()

  1. creates the buffered reader

  2. for loop -- reading the buffer for message from the server -- print out

  3. client dials the server

    go xyz(conn, p)

    fmt.Fprintf(conn, "Give me a hash to work on ...")


METHOD B, we have the order :

///////////// steps 2 and 3 are in the goroutine called by xyz()

  1. client dials the server

  2. creates buffered reader

  3. for loop -- reading the buffer for message from the server -- print out

    fmt.Fprintf(conn, "Give me a hash to work on ...")

    xyz(conn, p)




client.go

package main
import (
    "fmt"
    "net"
    "bufio"
)

func xyz(conn net.Conn, p []byte) {
  rd := bufio.NewReader(conn)
  for {
    _, err := rd.Read(p)
    if err == nil {
      fmt.Printf("SERVER : %s\n", p)
    } else {
        fmt.Printf("Some error %v\n", err)
    }
  }
}

func main() {
    p :=  make([]byte, 2048)
    conn, err := net.Dial("udp", "127.0.0.1:1234")
    if err != nil {
        fmt.Printf("Some error %v", err)
        return
    }

    go xyz(conn, p)
    fmt.Fprintf(conn, "Give me a hash to work on ...")
}

server.go

package main
import (
    "fmt"
    "net"
)


func sendResponse(conn *net.UDPConn, addr *net.UDPAddr, hash string) {
    _,err := conn.WriteToUDP([]byte("Hello, here is the hash  - " + hash), addr)
    if err != nil {
        fmt.Printf("Couldn't send response %v", err)
    }
}


func main() {
    hash := "36";
    p := make([]byte, 2048)
    addr := net.UDPAddr{
        Port: 1234,
        IP: net.ParseIP("127.0.0.1"),
    }
    ser, err := net.ListenUDP("udp", &addr)
    if err != nil {
        fmt.Printf("Some error %v\n", err)
        return
    }
    for {
        _, remoteaddr, err := ser.ReadFromUDP(p)
        fmt.Printf("CLIENT : %v : %s\n", remoteaddr, p)
        if err !=  nil {
            fmt.Printf("Some error  %v", err)
            continue
        }
        go sendResponse(ser, remoteaddr, hash)
    }
}

答案1

得分: 2

《Go编程语言规范》

Go语言规范中介绍了"go"语句的使用。

"go"语句可以将一个函数调用作为一个独立的并发线程或goroutine在同一地址空间中执行。

与普通的函数调用不同,程序执行不会等待被调用的函数完成。

client.go文件中使用"go"语句启动了名为xyz的goroutine,并继续执行main函数直到程序结束。程序不会等待xyz goroutine的运行或完成。

英文:

> The Go Programming Language Specification
>
> Go statements
>
> A "go" statement starts the execution of a function call as an
> independent concurrent thread of control, or goroutine, within the
> same address space.
>
> ... unlike with a regular call, program execution does not wait for
> the invoked function to complete.

client.go starts the goroutine xyz and then keeps going to the end of the main function which terminates the program. The program doesn't wait for the xyz goroutine to run or finish.

huangapple
  • 本文由 发表于 2017年4月21日 10:51:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/43533125.html
匿名

发表评论

匿名网友

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

确定