Golang中Channels的使用帮助
Golang中Channels的使用帮助 大家好, 我正在尝试学习通道,当我运行代码时,得到以下输出,第一次运行后变为大写。在playground中我得到的是全小写。运行版本为v1.10。 提前感谢。
google
ALTAVISTA
ALPHABET
BING
YAHOO
func main() {
names := []string{
"yahoo", "google", "altavista", "alphabet", "bing",
}
n := make(chan string, len(names))
for _, name := range names {
n <- name
go ToUpper(n)
}
for i := 0; i < len(names); i++ {
fmt.Println(<-n)
}
close(n)
}
func ToUpper(c chan string) {
u := strings.ToUpper(<-c)
c <- u
}
更多关于Golang中Channels的使用帮助的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你没有等待 goroutine 执行完成。最好使用无缓冲通道和等待组。最简单但不理想的解决方案是在 main() 函数末尾添加休眠时间。
另外,通道应当仅用于读取或写入,不要同时进行两种操作。
为输入和结果使用独立的通道。
在你的代码中,ToUpper协程和主协程中的fmt.Println循环从同一个通道读取数据,该通道同时包含输入数据和ToUpper()的输出结果。由于协程的运行顺序没有明确定义,因此结果是不确定的。
Playground首先运行fmt.Println循环,这就是你得到未处理输入作为输出的原因。当ToUpper协程运行时,通道已经为空。(除此之外,当主协程结束时,所有其他协程会立即停止,因此ToUpper协程可能永远不会运行。)
在你的本地机器上,fmt.Println()循环似乎在第一个ToUpper协程启动前至少运行了一次,这就是为什么你看到第一个输出未经处理的原因。
为输入值和结果值使用独立的通道应该能解决这个问题。同时要确保主协程等待其他协程完成。(例如参考sync/WaitGroup。)
感谢大家的建议。 我已经通过这个示例解决了问题。
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func getLength(url string) (int, error) {
resp, err := http.Get(url)
if err != nil {
return 0, nil
}
defer resp.Body.Close()
pageByte, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, nil
}
return len(pageByte), nil
}
func worker(urlCh, sizeCh chan string, id int) {
for {
url := <-urlCh
psize, err := getLength(url)
if err != nil {
os.Exit(1)
}
sizeCh <- fmt.Sprintf("%s length is %d (%d)", url, psize, id)
}
}
func generator(url string, urlCh chan string) {
urlCh <- url
}
func main() {
// url := "http://www.google.ca"
urls := []string{"http://www.golang.org", "http://www.google.ca",
"http://www.bing.com", "http://www.yahoo.ca", "http://www.toronto.ca"}
sizeCh := make(chan string)
urlCh := make(chan string)
for i := 0; i < 10; i++ {
go worker(urlCh, sizeCh, i)
}
for _, url := range urls {
go generator(url, urlCh)
}
for i := 0; i < len(urls); i++ {
fmt.Printf("%s\n", <-sizeCh)
}
}
在您的代码中,通道 n 是一个带缓冲的通道,容量为 len(names)(即5)。问题在于您在主 goroutine 中向通道发送所有字符串,然后启动 goroutine 来从通道接收并转换为大写,但 goroutine 的执行顺序是不确定的。这可能导致某些字符串在第一次运行后显示为大写,而其他运行可能不同,因为 goroutine 的调度顺序可能变化。
具体来说,在循环中:
- 您将每个
name发送到通道n。 - 然后启动一个 goroutine 调用
ToUpper,它从通道接收一个值,转换为大写,并发送回通道。
由于通道有缓冲,主 goroutine 可以立即发送所有值而不阻塞。然后,goroutine 并发地从通道接收,但它们的执行顺序会影响输出。例如,如果某个 goroutine 在另一个 goroutine 发送回大写值之前从通道接收,您可能会看到小写输出。在 Go Playground 上,由于环境限制,goroutine 的调度可能更一致,导致全小写。
要确保所有字符串都转换为大写,您应该重构代码,使每个字符串的处理独立,并避免在同一个通道上混合发送和接收。以下是修改后的示例,使用无缓冲通道和同步方式:
package main
import (
"fmt"
"strings"
)
func main() {
names := []string{
"yahoo", "google", "altavista", "alphabet", "bing",
}
n := make(chan string) // 无缓冲通道
// 启动 goroutine 来处理每个名称
for _, name := range names {
go func(name string) {
u := strings.ToUpper(name)
n <- u
}(name)
}
// 从通道接收所有结果
for i := 0; i < len(names); i++ {
fmt.Println(<-n)
}
close(n)
}
在这个版本中:
- 使用无缓冲通道
n。 - 对每个
name启动一个 goroutine,直接在 goroutine 中转换为大写并发送到通道。 - 主 goroutine 从通道接收所有结果。
输出应该始终是全大写的,例如:
ALTAVISTA
ALPHABET
BING
YAHOO
GOOGLE
注意:输出顺序可能因 goroutine 完成时间而异,但所有字符串都会是大写。如果您需要保持原始顺序,可以使用带索引的 goroutine 和结果切片。

