计算机系统应用教程网站

网站首页 > 技术文章 正文

GoLang 初识Channel管道 golang 命名管道

btikc 2024-10-12 10:47:39 技术文章 22 ℃ 0 评论

当程序中使用goroutine来操作一个全局变量时,拿map来举例,同时使用多个协程来向map中写入数据,此时代码可能会报错.

代码片段:

package main

import (
	"fmt"
)

var (
	result = make(map[int]int, 10)
)

func Strata(num int) {
	res := 1
	for i := 1; i <= num ; i ++ {
		res *= i
	}
	// 将结果放入map中
	result[num] = res
}	

// 使用全局变量加锁方式 解决了 并发写入问题,但是 不知道具体 协程什么时候运行结束
// 那么这个问题怎么解决呢?

func main() {

	// 这里开启多个协程, 完成这个任务
	// 问题1: fatal error: concurrent map writes (存在并发安全问题)
	// 问题2: 通过 go build -race main.go   >> 拿到main.exe,并运行 >>  Found 2 data race(s) 发现有两个存在竞争关系
	for i := 1; i <= 20 ; i ++ {
		go Strata(i)
	}
	// 输出结果
	for i, v := range result {
		fmt.Printf("result[%v]=%v \n", i, v)
	}
}

报错信息:

fatal error: concurrent map writes

上面信息表示,当前map存在并发安全问题。


通过上面代码引出,有两种解决办法,一是使用golang提供的 sync包中的Mutex互斥锁来解决,在添加前后加锁。

另一种办法则是使用channel(管道)。channel是线程安全,多个协程操作同一管道时,不会发生资源竞争问题。 建议:使用channel时最好使用存放同一种数据类型。


Channel 基本操作

package main
import (
	"fmt"
)

type Stu struct {
	Name string
	Age int
}

func main() {
	// 定义一个管道
	var newChan chan interface{}
	newChan = make(chan interface{}, 10)

	newChan <- "Mic"
	newChan <- 10

	// 添加一个Cat结构体
	stu := Stu{"Tom", 20}
  // 添加一个元素到newChan的channel中
	newChan <- stu

	// 如果想要取得最后一个元素,则先把前面的元素移除
	<- newChan
	<- newChan

	newStu := <- newChan

	// 打印newStu类型以及值
	// 打印结果newStu=main.Stu, newStu={Tom 20} 
	fmt.Printf("newStu=%T, newStu=%v \n", newStu, newStu)

	// 打印newStu的值
	// 这样取直接报错: newStu.Name undefined (type interface{} has no field or method Name)
	// 因为取出的是一个空接口, 表示没有Name这个属性。
	//fmt.Println("newStu.Name=", newStu.Name)

	// 解决上面的问题,需要使用到前面的说过的 类型断言

	c := newStu.(Stu)
	fmt.Println("c.Name=", c.Name) // 这样一来就不会报错了。

}

输出结果:

newStu=main.Stu, newStu={Tom 20} 
c.Name= Tom

熟悉了Channel的基本使用, 则在使用协程操作时, 只需定义一个全局的管道,则就可以继续操作。

其他操作,后续继续学习。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表