管道底层是一个环形队列(先进先出);
管道是Go语言在语言级别上提供的goroutine间的通讯方式,我们可以使用channel在多个goroutine之间传递消息。channel是进程内的通讯方式,是不支持跨进程通信的,如果需要进程间通讯的话,可以使用Socket等网络方式。
var ch chan int //声明一个管道 (先进先出)
//初始化管道
ch = make(chan int, 9)//实例化一个管道
package main
import "fmt"
func channelTest() {
var ch chan int //声明一个管道 (先进先出)
//初始化管道
ch = make(chan int, 9)//实例化一个管道
fmt.Println(ch)
//向管道中加入数据 向队尾加入数据
ch <- 1
ch <- 2
ch <- 3
//取出数据 从队尾取出数据
value := <-ch
fmt.Println(value)
value = <-ch
fmt.Println(value)
value = <-ch
fmt.Println(value)
ch<-100
ch<-200
ch<-300
close(ch)
//管道的遍历 遍历之前必须关闭管道否则会报错---fatal error: all goroutines are asleep - deadlock!
for els := range ch {
fmt.Println(els)
}
}
func main() {
channelTest()
}
//取出数据 从队尾取出数据
value := <-ch
fmt.Println(value)
value = <-ch
fmt.Println(value)
value = <-ch
fmt.Println(value)
ch<-100
ch<-200
ch<-300
1,使用range的方式,需要先关闭管道
2,使用普通方式:
func main() {
var g chan int
g = make(chan int, 10)
for i := 0; i < cap(g); i++ {
g <- 10
}
//遍历管道;
for i := 0; i < len(g); i++ {
v := <-g
fmt.Println(v)
}
close(g)
//这里应该是空,因为已经被取走了
for i2 := range g {
fmt.Println(i2)
}
}
管道一旦关闭,就不能再往里面添加数据了
如果管道中没有数据,那么从管道中读取数据会导致程序阻塞,直到有数据–
var ch chan int
func putM(ch chan int) {
ch <- 100
ch <- 2
ch <- 200
}
func getM(ch chan int) {
v := <-ch
fmt.Println(v)
v = <-ch
fmt.Println(v)
v = <-ch
fmt.Println(v)
v = <-ch
fmt.Println(v)
}
func main() {
var ch chan int
ch = make(chan int, 10)
ch <- 1
ch <- 2
ch <- 3
putM(ch)
getM(ch)
}
在运行阶段,go会直接抛一个异常
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.putM(...)
D:/GoData/eight/ChannelMain.go:40
main.main()
D:/GoData/eight/ChannelMain.go:60 +0x90
Process finished with the exit code 2
这是因为目前只有一个线程去管道里读取数据,多了的没办法拿走,此时会产生一个死锁出来;
在看一个代码:一个往管道里面放,另一个来取
package main
import (
"fmt"
"math/rand"
"time"
)
const name = "小明"
// 数据生产者
func producer(header string, channel chan<- string) {
fmt.Println(name)
for {
channel <- fmt.Sprintln("%s:%v", header, rand.Int31())
time.Sleep(time.Second)
}
}
//数据消费者
func customer(channel <-chan string) {
for {
message := <-channel
fmt.Println(message)
}
}
/*
*
这是程序执行的入口
*/
func main() {
channel := make(chan string) //实例化一个子字符串通道
go producer("cat", channel)
go producer("dog", channel)
customer(channel)
}
go中结构体的定义:
package main
import (
"fmt"
"time"
)
// 定义一个结构体 匿名结构体
var man struct {
name string
age int
}
//定义了一个结构体类型为Human
type Human struct {
name string
age int
birthday time.Time
country string
addr, love string
}
//为结构体编写一个方法
//这样我们也可以向java那样编写一个类似于类这样一个...
func (H Human) Say(word string) {
fmt.Println(man)
fmt.Println(H.name, H.age, H.birthday, H.country, "说", word)
}
func main() {
//准备一个结构体
xm := Human{name: "小明", age: 19, birthday: time.Now(), country: "China"}
xm.Say("我爱中国")
}
go中的结构体是面向对象编程的一个关键点;
在go中我们可以返回一个结构指针,这么做的目的其实是为了避免值的拷贝;
例如:
取得结构体的指针方式:
func test() {
//声明一个结构体为dog
var dog Animal
//获得这个结构体的指针 即d是一个指针变量
d := &dog
fmt.Printf("%v %p\n",d,d)
//获得这个结构体的指针 即d是一个指针变量
d = &Animal{
}
fmt.Printf("%v %p\n",d,d)
//也可以为这个结构体中的属性赋值
d = &Animal{
name: "王啊啊",
age: 2,
}
fmt.Printf("%v %p\n",d,d)
//或者可以通过new()函数
//通过new()函数来获取指针
d=new(Animal)
fmt.Printf("%v %p\n",d,d)
}
观察结果我们可以看到,当我们不赋值的时候
d := &dog
d = &Animal{
}
是等价的;
通过结构体,有了类型上的区分,有了类型上的区分就有了类似java中的构造函数:
//这个可以看作是构造函数
func NewPerson(name string ,age int) *Person {
return &Person{
name: "小明",
age: 10,
}
测试一下代码:
//这个可以看作是构造函数
//返回的是Person的值
func NewPerson(name string, age int) *Person {
return &Person{
name: name,
age: age,
}
}
func main(){
fmt.Println("-----------------------------------")
fmt.Println(NewPerson("小红", 22))
}
返回值是指针和不是指针的区别:
首先对于基础的类型,没有什么太大影响,如果是对于引用类型,则就有很大的影响:
举个例子—>
源代码如下,
// 声明的全局变量
var name = "小花"
var age = 17
func main() {
fmt.Println(NewPerson(name , age ))
fmt.Println(newPerson(name , age ))
fmt.Println("\n")
fmt.Printf("%s %d name的地址%p age的地址%p\n", name, age, &name, &age)
fmt.Printf("name的地址 %p age的地址 %p\n", &(NewPerson(
name,
age,
).name), &(NewPerson(
name,
age,
).age))
//fmt.Printf("name的地址 %p age的地址 %p\n", &(newPerson(name, age).name), &(newPerson(name, age).age))
}
// 这个可以看作是构造函数
func NewPerson(name string, age int) *Person {
//fmt.Printf("%s %d name的地址%p age的地址%p\n", name, age, &name, &age)
return &Person{
name: name,
age: age,
}
}
func newPerson(name string, age int) Person {
//fmt.Printf("%s %d name的地址%p age的地址%p\n", name, age, &name, &age)
return Person{
name: name,
age: age,
}
}
如果结构体中含有匿名变量,
像这样:
只能有一个 可以不写名字的同类型变量;
可以看到 对于下划线的不配拥有内存空间
func fun() {
//niming.string="你哈珀"
//niming.int=127
niming.name="匿名"
fmt.Printf(" string %p\n ",&niming.string)
fmt.Printf(" int %p\n ",&niming.int)
fmt.Printf(" name %p\n ",&niming.name)
}