Channel的定义、写入、读取、关闭与遍历

2023-11-18

目录

1.Channel基础

为什么要引入Channel ?

  • 使用全局变量加锁同步可以来解决goroutine的通讯,但不完美,原因如下:
  • 主线程在等待所有goroutine全部完成的时间很难确定,我们这里设置10秒,只能估算。如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随主线程的退出而销毁。通过全局变量加锁同步来实现通讯,也并不利于多个协程对全局变量的读写操作。所以需要一个新的通讯机制来实现自动实现goroutine之间的通讯,主线程可以自动捕获goroutine结束,channel即可满足这种需求

Channel简介

  1. channle本质就是一个数据结构-队列
  2. 数据是先进先出【FIFO:first in first out】
  3. 线程安全,多goroutine 访问时,不需要加锁,就是说channel 本身就是线程安全的
  4. channel有类型的,一个string的channel只能存放 string类型数据。

channel的定义/声明

var 变量名 chan 变量类型

//channel是有类型的intChan 只能写入整数int
var intchan chan int 
//mapChan用于存放map[int]string类型
var mapchan chan map[int]string 

channel 必须初始化才能写入数据,即make后才能使用

//创建一个可以存放3个int类型的管道
intChan = make(chan int, 3)

声明的同时初始化

// 创建容量为 10 的 channel
 mapchan := make(chan map[string]string, 10) 

channel的数据类型:引用类型

//看看intChan是什么
	fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n", intChan, &intChan)
intChan 的值=0xc000108080 intChan本身的地址=0xc00000a028
//intchan 指向存放数据的地址

向channel中写入数据

int型

// 向管道写入数据
	intChan<- 10
	
	num := 211
	intChan<- num
	intChan<- 50
	
	// 如果从channel取出数据后,可以继续放入
	<-intChan
	intChan<- 98
	
	//注意点, 当我们给管道中写入数据时,不能超过其容量,数据放满后就不能再放入了

从channel中读取数据

   //从管道中读取数据
	var num2 int
	num2 = <-intChan
	fmt.Println("num2=", num2)
	fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 2, 3

	num3 := <-intChan
	num4 := <-intChan
	fmt.Println("num3=", num3, "num4=", num4)  // num3= 50 num4= 98  
	
	// 在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock
	//num5 := <-intChan
	// fmt.Println( "num5=", num5)
	//fatal error: all goroutines are asleep - deadlock!	

2 channel的遍历与关闭

channel的关闭

使用内置函数close关闭channel

使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然
可以从该channel读取数据。
在 Go 中,我们可以使用内置函数 close 来关闭一个 channel。关闭一个 channel 后,就不能再向该 channel 中写入数据了,但仍然可以从该 channel 中读取数据。当从一个已经关闭的 channel 中读取数据时,会得到该 channel 类型的零值。

一个如何关闭channel的例子

package main

import "fmt"

func main() {
    // 创建一个容量为 10 的整数类型的 channel
    intChan := make(chan int, 10)

    // 向 channel 中写入 5 个整数
    for i := 1; i <= 5; i++ {
        intChan <- i
    }

    // 关闭 channel
    close(intChan)

    // 从 channel 中读取所有整数
    for {
        // 从 channel 中读取一个整数
        value, ok := <-intChan

        // 如果 channel 已经关闭且里面没有数据了,就退出循环
        if !ok {
            break
        }

        // 输出读取的整数
        fmt.Printf("Read a value from intChan: %d\n", value)
    }

    // 再次从 channel 中读取数据,会得到零值
    value, ok := <-intChan
    fmt.Printf("Read a value from intChan after close: %d, %v\n", value, ok)
}

在这段代码中,我们创建了一个容量为 10 的整数类型的 channel intChan。然后我们向 intChan channel 中写入了 5 个整数。接着,我们使用 close 函数关闭了 intChan channel。最后,我们使用一个循环从 intChan channel 中读取所有整数,并将它们输出。在读取完所有整数后,我们再次从 intChan channel 中读取数据,这时会得到一个整数类型的零值和一个表示 channel 是否关闭的布尔值 false。

输出结果如下:

Read a value from intChan: 1
Read a value from intChan: 2
Read a value from intChan: 3
Read a value from intChan: 4
Read a value from intChan: 5
Read a value from intChan after close: 0, false

向已关闭的channel中继续写入数据

在 Go 中,当一个 channel 被关闭后,就不能再向该 channel 中写入数据了,否则会导致 panic。

下面是一个演示如何关闭 channel 的例子,同时在关闭后向该 channel 写入数据:

package main

import "fmt"

func main() {
    // 创建一个容量为 10 的整数类型的 channel
    intChan := make(chan int, 10)

    // 向 channel 中写入 5 个整数
    for i := 1; i <= 5; i++ {
        intChan <- i
    }

    // 关闭 channel
    close(intChan)

    // 向已关闭的 channel 写入数据,会导致 panic
    intChan <- 6
    fmt.Println("write data to closed channel")

    // 从 channel 中读取所有整数
    for {
        // 从 channel 中读取一个整数
        value, ok := <-intChan

        // 如果 channel 已经关闭且里面没有数据了,就退出循环
        if !ok {
            break
        }

        // 输出读取的整数
        fmt.Printf("Read a value from intChan: %d\n", value)
    }
}

运行上面的代码会产生如下的 panic 信息:

panic: send on closed channel

goroutine 1 [running]:
main.main()
        /path/to/main.go:15 +0x10b
exit status 2

可以看到,向已关闭的 channel 中写入数据会导致 panic,程序崩溃。

在这段代码中,我们创建了一个容量为 10 的整数类型的 channel intChan。然后我们向 intChan channel 中写入了 5 个整数。接着,我们使用 close 函数关闭了 intChan channel。接着,我们向已关闭的 intChan channel 中写入了整数 6,这时会导致 panic。最后,我们使用一个循环从 intChan channel 中读取所有整数,并将它们输出。

channel的遍历

channel遍历的方式

在 Go 中,可以通过 for 循环遍历 channel 中的数据,有以下几种方式:

1.使用 range 关键字遍历 channel
// 创建一个容量为 3 的整数类型的 channel
intChan := make(chan int, 3)

// 向 channel 中写入 3 个整数
intChan <- 1
intChan <- 2
intChan <- 3

// 使用 range 关键字遍历 channel
for value := range intChan {
    fmt.Printf("Read a value from intChan: %d\n", value)
}

在这段代码中,我们创建了一个容量为 3 的整数类型的 channel intChan,并向其中写入了 3 个整数。接着,我们使用 range 关键字遍历了 intChan channel 中的所有数据,并将它们输出。当 channel 被关闭后,使用 range 关键字遍历 channel 会自动退出循环。

2.使用 for 循环和 select 语句遍历 channel
// 创建一个容量为 3 的整数类型的 channel
intChan := make(chan int, 3)

// 向 channel 中写入 3 个整数
intChan <- 1
intChan <- 2
intChan <- 3

// 使用 for 循环和 select 语句遍历 channel
for {
    select {
    case value, ok := <-intChan:
        // 如果 channel 已经关闭且里面没有数据了,就退出循环
        if !ok {
            return
        }

        // 输出读取的整数
        fmt.Printf("Read a value from intChan: %d\n", value)
    }
}

在这段代码中,我们创建了一个容量为 3 的整数类型的 channel intChan,并向其中写入了 3 个整数。接着,我们使用 for 循环和 select 语句遍历了 intChan channel 中的所有数据,并将它们输出。当 channel 被关闭后,使用 for 循环和 select 语句遍历 channel 需要手动退出循环。

3 使用 len 函数和 for 循环遍历 channel(难点)
1) i < len(intChan) 陷入死循环
// 创建一个容量为 3 的整数类型的 channel
intChan := make(chan int, 3)

// 向 channel 中写入 3 个整数
intChan <- 1
intChan <- 2
intChan <- 3

// 使用 len 函数和 for 循环遍历 channel
for i := 0; i < len(intChan); i++ {
    // 从 channel 中读取一个整数
    value := <-intChan

    // 输出读取的整数
    fmt.Printf("Read a value from intChan: %d\n", value)
}

在这段代码中,我们创建了一个容量为 3 的整数类型的 channel intChan,并向其中写入了 3 个整数。接着,我们使用 len 函数和 for

但是,这段代码会输出前两个整数,然后进入死循环,因为在for循环中使用了len(intChan)作为循环条件,但是在循环的过程中我们不断地从channel中读取数据,导致channel的长度始终保持为1,不会发生变化,从而导致for循环无法退出。

解决方案1:使用cap(intChan)函数获取channel的容量

正确的做法是在循环条件中使用cap(intChan)函数获取channel的容量,如下所示:

// 创建一个容量为 3 的整数类型的 channel
intChan := make(chan int, 3)

// 向 channel 中写入 3 个整数
intChan <- 1
intChan <- 2
intChan <- 3

// 使用 len 函数和 for 循环遍历 channel
for i := 0; i < cap(intChan); i++ {
    // 从 channel 中读取一个整数
    value := <-intChan

    // 输出读取的整数
    fmt.Printf("Read a value from intChan: %d\n", value)
}

这样就可以正确地遍历整个channel,输出所有的整数了。

2) 只等读到一半的数据
package main

import "fmt"

func main() {
	// 创建一个容量为 3 的整数类型的 channel
	ch := make(chan int, 30)

	// 向 channel 中写入 30 个整数
	for i := 0; i < 30; i++ {
		ch <- i
	}
	close(ch)

	// 使用 len 函数和 for 循环遍历 channel
	for i := 0; i < len(ch); i++ {
		// 从 channel 中读取一个整数
		value := <-ch
		// 输出读取的整数
		fmt.Printf("Read a value from intChan: %d\n", value)
	}

	//for len(ch) > 0 {
	//	fmt.Println(<-ch)
	//}
}

输出结果

Read a value from intChan: 0
Read a value from intChan: 1 
Read a value from intChan: 2 
Read a value from intChan: 3 
Read a value from intChan: 4 
Read a value from intChan: 5 
Read a value from intChan: 6 
Read a value from intChan: 7 
Read a value from intChan: 8 
Read a value from intChan: 9 
Read a value from intChan: 10
Read a value from intChan: 11
Read a value from intChan: 12
Read a value from intChan: 13
Read a value from intChan: 14
解决方案2:for len(ch) > 0 {}

如何使用len()得到正确结果

for len(ch) > 0{}
package main

import "fmt"

func main() {
	// 创建一个容量为 3 的整数类型的 channel
	ch := make(chan int, 30)

	// 向 channel 中写入 30 个整数
	for i := 0; i < 30; i++ {
		ch <- i
	}
	close(ch)

	// 使用 len 函数和 for 循环遍历 channel
	//for i := 0; i < len(ch); i++ {
	//	// 从 channel 中读取一个整数
	//	value := <-ch
	//	// 输出读取的整数
	//	fmt.Printf("Read a value from intChan: %d\n", value)
	//}

	for len(ch) > 0 {
		value := <-ch
		// 输出读取的整数
		fmt.Printf("Read a value from intChan: %d\n", value)
	}
}

结果如下

Read a value from intChan: 0
Read a value from intChan: 1 
Read a value from intChan: 2 
Read a value from intChan: 3 
Read a value from intChan: 4 
Read a value from intChan: 5 
Read a value from intChan: 6 
Read a value from intChan: 7 
Read a value from intChan: 8 
Read a value from intChan: 9 
Read a value from intChan: 10
Read a value from intChan: 11
Read a value from intChan: 12
Read a value from intChan: 13
Read a value from intChan: 14
Read a value from intChan: 15
Read a value from intChan: 16
Read a value from intChan: 17
Read a value from intChan: 18
Read a value from intChan: 19
Read a value from intChan: 20
Read a value from intChan: 21
Read a value from intChan: 22
Read a value from intChan: 23
Read a value from intChan: 24
Read a value from intChan: 25
Read a value from intChan: 26
Read a value from intChan: 27
Read a value from intChan: 28
Read a value from intChan: 29

思考:channel的遍历中for len(ch) > 0{ } 能得到全部结果,然而for i := 0; i < len(ch); i++{ }只能得到一半结果

在使用for i := 0; i < len(ch); i++遍历channel时,由于channel在遍历过程中长度是动态变化的,因此可能只能读取到channel的一部分数据。具体来说,当channel中的数据量大于循环开始时的长度时,只能读取到前一半的数据。同时,如前面例子所示,当len(ch)=3时,会陷入死循环。

相比之下,for len(ch) > 0 {} 的循环条件是根据 channel 的实际长度来判断的,因此只要 channel 中还有元素,循环就会继续执行,直到 channel 中的所有元素都被读取完毕。因此,这种方式可以保证读取到全部的结果。

遍历时,如果channel没有关闭,则会出现deadlock的错误

channel支持for–range的方式进行遍历,请注意两个细节

  1. 在遍历时,如果channel 已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。如前文所述;
    2)在遍历时,如果channel没有关闭,则回出现deadlock的错误

一个会导致deadlock错误的例子:

package main

import "fmt"

func main() {
    ch := make(chan int)
    ch <- 1
    ch <- 2
    ch <- 3

    for i := range ch {
        fmt.Println(i)
    }
}

在这个例子中,我们创建了一个int类型的channel,并向该channel中写入了三个数据。然后我们使用for range循环遍历该channel,并输出其中的数据。但是由于我们没有关闭channel,当循环遍历完前三个数据后,程序会一直阻塞等待新的数据,导致deadlock错误。

fatal error: all goroutines are asleep - deadlock!

为了避免这种错误,我们可以使用close函数在遍历完所有数据后手动关闭channel,例如:

package main

import "fmt"

func main() {
    ch := make(chan int)
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)

    for i := range ch {
        fmt.Println(i)
    }
}

在这个例子中,我们在循环遍历之前,使用close函数手动关闭了channel。这样,在遍历完所有数据后,for range循环会自动退出,避免了deadlock错误的发生。

3 实例演示:定义map型channel,并进行数据的读写

map型channel

package main

import "fmt"

func main() {
    ch := make(chan map[string]string, 10) // 创建容量为 10 的 channel

    // 向 channel 中输入 6 个数据
    for i := 1; i <= 6; i++ {
        data := make(map[string]string)
        data["key"] = fmt.Sprintf("value %d", i)
        ch <- data
    }

    // 从 channel 中取出并输出 6 个数据
    for i := 1; i <= 6; i++ {
        data := <-ch
        fmt.Println(data["key"])
    }
}

value 1
value 2
value 3
value 4
value 5
value 6


map型channel2:

package main

import "fmt"

func main() {
    mapchan := make(chan map[string]string, 10) // 创建容量为 10 的 channel

    // 创建两个 map 变量 m1 和 m2,并向其中各写入 5 个数据
    m1 := make(map[string]string, 20)
    m1["key1"] = "value1"
    m1["key2"] = "value2"
    m1["key3"] = "value3"
    m1["key4"] = "value4"
    m1["key5"] = "value5"

    m2 := make(map[string]string, 20)
    m2["key6"] = "value6"
    m2["key7"] = "value7"
    m2["key8"] = "value8"
    m2["key9"] = "value9"
    m2["key10"] = "value10"

    // 将 m1 和 m2 中的数据写入到 mapchan channel 中
    mapchan <- m1
    mapchan <- m2

    // 从 mapchan channel 中取出并输出所有数据
    for len(mapchan) > 0 {
        m := <-mapchan
        for k, v := range m {
            fmt.Printf("%s: %s\n", k, v)
        }
    }
}

输出结果

key1: value1
key2: value2
key3: value3
key4: value4
key5: value5
key6: value6
key7: value7
key8: value8
key9: value9
key10: value10

在这段代码中,我们创建了一个容量为 10 的 mapchan channel,并创建了两个 map 变量 m1 和 m2,每个变量中各包含 5 个键值对。接下来,将 m1 和 m2 中的数据分别写入到 mapchan channel 中。然后使用一个循环从 mapchan channel 中读取数据,直到 channel 中没有剩余的数据。在循环体中,我们取出 channel 中的一个 map 变量 m,并遍历其中的键值对并输出它们。需要注意的是,在循环中我们使用 len(mapchan) > 0 来判断 channel 中是否还有剩余的数据,因为 len(mapchan) 表示 channel 中剩余的数据数量。由于我们在循环体中读取了所有数据,因此在循环结束后,mapchan channel 中不再有任何数据。

Struct型channel

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    // 创建一个容量为 3 的 Person 类型的 channel
    personChan := make(chan Person, 3)

    // 向 channel 中写入 3 个 Person 类型的变量
    person1 := Person{"Tom", 18}
    person2 := Person{"Jerry", 19}
    person3 := Person{"Mike", 20}

    personChan <- person1
    personChan <- person2
    personChan <- person3

    // 从 channel 中取出并输出所有 Person 类型的变量
    for len(personChan) > 0 {
        person := <-personChan
        fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
    }
}

通过空接口实现存放任意数据类型的channel

可以存放任意数据类型的channel:通过空接口实现
在下面这个例子中,我们将多个不同类型的数据写入了 dataChan channel,然后按照它们写入的顺序一个一个地取出来。输出结果显示了每个元素的值,其中包括整数、字符串、布尔值、浮点数、整数数组、空的映射、包含一个整数字段的结构体、空指针、字节数组以及包含字符串、整数和布尔值的接口切片。由于我们使用了 interface{} 类型,可以存储任何类型的数据。

package main

import "fmt"

func main() {
    // 创建一个容量为 10 的 interface{} 类型的 channel
    dataChan := make(chan interface{}, 10)

    // 向 channel 中写入 10 个任意类型的数据
    dataChan <- 1
    dataChan <- "hello"
    dataChan <- true
    dataChan <- 3.14
    dataChan <- []int{1, 2, 3}
    dataChan <- make(map[string]int)
    dataChan <- struct{ X int }{X: 10}
    dataChan <- nil
    dataChan <- []byte("world")
    dataChan <- []interface{}{"a", 1, false}

    // 从 channel 中取出并输出所有数据
    for len(dataChan) > 0 {
        data := <-dataChan
        fmt.Printf("%v\n", data)
    }
}

1
hello
true
3.14
[1 2 3]
map[]
{10}
<nil>
[119 111 114 108 100]
[a 1 false]

空接口型channel与类型断言

在 Go 中,我们可以使用空接口类型 interface{} 表示任意类型的值。当我们将一个具体类型的值存储到空接口类型的变量中时,这个变量会自动地被转换为 interface{} 类型。但是,在从空接口类型的变量中取出具体类型的值时,我们需要使用类型断言来将其转换回原来的类型。

下面是一个在空接口类型的 channel 中使用类型断言的例子:

package main

import "fmt"

func main() {
    // 创建一个容量为 10 的空接口类型的 channel
    dataChan := make(chan interface{}, 10)

    // 将多个不同类型的数据写入 channel 中
    dataChan <- 1
    dataChan <- "hello"
    dataChan <- []int{1, 2, 3}
    dataChan <- struct{ X int }{X: 10}

    // 从 channel 中取出数据并进行类型断言
    for len(dataChan) > 0 {
        data := <-dataChan
        switch v := data.(type) {
        case int:
            fmt.Printf("Got an int: %d\n", v)
        case string:
            fmt.Printf("Got a string: %s\n", v)
        case []int:
            fmt.Printf("Got an int slice: %v\n", v)
        case struct{ X int }:
            fmt.Printf("Got a struct: %+v\n", v)
        default:
            fmt.Printf("Got something else: %v\n", v)
        }
    }
}

在这段代码中,我们创建了一个容量为 10 的空接口类型的 channel dataChan。然后我们向 dataChan channel 中写入了 4 个不同类型的数据,包括 int、string、[]int 和 struct{X int}。最后,我们使用一个循环从 dataChan channel 中取出所有数据,并根据其类型进行类型断言。如果数据是 int 类型,我们就输出 "Got an int: " 和该值;如果数据是 string 类型,我们就输出 "Got a string: " 和该值;以此类推。

输出结果如下:

Got an int: 1
Got a string: hello
Got an int slice: [1 2 3]
Got a struct: {X:10}

在这个例子中,我们使用了类型断言来将从 dataChan channel 中取出的值转换回原来的类型。由于我们在写入数据时将它们转换成了空接口类型,因此在取出数据时需要使用类型断言将它们转换回原来的类型。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Channel的定义、写入、读取、关闭与遍历 的相关文章

  • 测试 gRPC 服务

    我想测试用 Go 编写的 gRPC 服务 我使用的示例是 Hello World 服务器示例grpc go 仓库 https github com grpc grpc go blob master examples helloworld g
  • 关于编写惯用的 Golang 的建议

    我正在掌握 Golang 的做事方式 首先是一些示例代码 package main import log os func logIt s string f os OpenFile errors log os O RDWR os O CREA
  • 如何构建一个不链接到 musl libc 的 go 可执行文件

    So 官方的 Go 构建容器基于 Alpine 高山用途musl https www musl libc org 作为 libc 而不是 glibc 我需要在容器中构建一个可以在使用 glibc 的 Ubuntu 上运行的 Go 可执行文件
  • Go 指针 - 通过指针将值附加到切片

    我有一个 struct ProductData 及其实例 p 它有一个切片属性 type ProductInfo struct TopAttributes map string interface 我想设置 TopAttributes 如下
  • 结构体到磁盘的高效 Go 序列化

    我的任务是将 C 代码替换为 Go 而且我对 Go API 还很陌生 我正在使用 gob 将数百个键 值条目编码到磁盘页面 但 gob 编码有太多不需要的膨胀 package main import bytes encoding gob f
  • Golang:如何在HTTP客户端的TLS配置中指定证书

    我有一个证书文件 该位置是 usr abc my crt我想将该证书用于我的 tls 配置 以便我的 http 客户端在与其他服务器通信时使用该证书 我当前的代码如下 mTLSConfig tls Config CipherSuites u
  • 使用生成的 Golang DLL 返回字符串或 *C.Char

    我一直在努力追随z505 goDLL https github com z505 goDLL回购并遇到了一个大问题 该方法无法返回字符串 我无法读取结果的输出变量 这是我到目前为止使用的代码 Go 完整代码https play golang
  • golang 中 *(*int)(nil) = 0 是什么意思?

    我注意到有一行 int nil 0在功能上throw https github com golang go blob master src runtime panic go L1113 go nosplit func throw s str
  • 在 Go 中解析多个 JSON 对象

    可以使用以下方法轻松解析如下对象encoding json包裹 something foo something else bar 我面临的问题是当服务器返回多个字典时 如下所示 something foo something else ba
  • 为什么 Go 中不允许在包级别声明短变量?

    这是允许的 package main var a 3 但这不是 package main a 3 为什么不 为什么不能将函数外部的短变量声明视为没有类型的常规声明 只是为了简化解析 根据伊恩 兰斯 泰勒的说法这个线程 https group
  • Go 中的数据竞争:为什么会在 10-11 毫秒以下发生?

    这是我运行的代码 package main import fmt time const delay 9 time Millisecond func main n 0 go func time Sleep delay n fmt Printl
  • 在 OSX 上交叉编译 Go?

    我正在尝试在 OSX 上交叉编译 go 应用程序以构建适用于 Windows 和 Linux 的二进制文件 我已经阅读了网上能找到的所有内容 我发现的最接近的例子已经发布在 除了疯狂邮件列表上许多未完成的讨论之外 http solovyov
  • golang中默认的HTTP拨号超时值

    我正在运行 golang http 客户端来对服务器进行压力测试 有时我会收到错误 拨号 tcp 161 170 xx xxx 80 操作超时 错误 我认为这是 HTTP 客户端超时 我正在考虑增加超时值https stackoverflo
  • 记录 http.ResponseWriter 内容

    Premise 我发现了类似的问题 但不适用于我的情况 因此请不要将其标记为重复 我在 Go 中有一个 HTTP 服务器 并且创建了一个中间件记录请求 响应时间 我也想记录响应 我用过httputil DumpRequest在一个名为的函数
  • 将 Websocket 消息发送到 Go 中的特定通道(使用 Gorilla)

    我对 Go 很陌生 并且发现自己使用套接字作为我的第一个项目 这是一个多余的问题 但我无法理解如何将 websocket 更新发送到 Go 中的特定通道 使用 Gorilla 我在用此链接中的代码示例 https github com go
  • 无法从另一个标签的源代码构建和安装 go

    我正在尝试使用此从源代码构建和安装 go文档 https go dev doc install source 当我喜欢以下内容时 这效果很好 git clone https go googlesource com go goroot cd
  • 防止使用 golang 服务器访问文件夹中的文件

    我在 golang 中有一个服务器可以处理这样的文件夹路径 fs http FileServer http Dir assets http Handle Images fs http ListenAndServe 8000 nil 但在这个
  • 无法将字符串解组为 int64 类型的 Go 值

    我有结构 type tySurvey struct Id int64 json id omitempty Name string json name omitempty I do json Marshal在 HTML 页面中写入 JSON
  • 如何使用 go web 服务器提供静态 html 文件?

    如何使用 go web 服务器提供 index html 或其他静态 HTML 文件 我只想要一个基本的静态 HTML 文件 例如一篇文章 我可以从 Go Web 服务器提供该文件 HTML 应该可以在 go 程序之外进行修改 就像使用 H
  • Bash脚本无法执行Go命令

    我正在尝试编写一个 bash 脚本来自动在不同的目录中运行 go get install 相关部分在这里 cd web go get cd web go install cd services go get cd services go i

随机推荐

  • MySQL索引篇

    目录 MySQL索引 一 怎么知道一条SQL语句有没有使用索引 二 如何排查慢查询 三 索引失效以及为什么失效 四 索引为什么能提高查询性能 五 为何选择B 树而不是B树 六 索引分类 七 什么时候创建以及什么时候不需要索引 八 索引优化
  • Python PyQt5(三)添加控件,绑定简单事件处理函数

    coding utf 8 Author BlueSand Email slxxfl000 163 com Web www lzmath cn Blog https blog csdn net weixin 41810846 Date 201
  • Leetcode 160. 相交链表 解题思路及C++实现

    解题思路 先将两个链表构建成一个环 定义两个快慢指针 当它们相遇时 将fast指针从头结点往后遍历 每次走一步 当这两个指针再次相遇时 该节点就是相交节点 Definition for singly linked list struct L
  • Verilog中forever、repeat、while、for四类循环语句(含Verilog实例)

    当搭建FPGA逻辑时 使用循环语句可以使语句更加简洁易懂 Verilog中存在四类循环语句 如标题 几种循环语句的具体介绍和用法如下 1 forever 连续的执行语句 语法格式 forever
  • 【算法入门】什么是时间复杂度和空间复杂度,最优解

    如何评价算法复杂度 时间复杂度 额外空间复杂度 常数操作 常数操作 常数操作 执行时间固定和数据量没有关系的运算操作 如果和数据量有关就不是常数操作 运算 数组寻址 数组里获取3位置和3000w位置数据时间相等 1 1 和100w 100w
  • unity3D期末作业捕鱼游戏,适合初学者学习使用,包含源程序所有文件

    虚拟现实期末作业捕鱼游戏 免积分下载 点我下载资源 有按钮 背景音乐 可以发射炮弹捕鱼 可以选择难度 可以调节音乐声音大小 有游戏加载进度条 详细情况请看如下动态图 点我下载资源
  • DataFrame添加列名,查看均值等,seaborn

    查看数据 seaborn画图简单好看 看两两特征的关系 对角线是自己和自己 dropna 处理缺失值
  • 设计模式 之 状态模式

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 设计模式 之 状态模式 概念 类的行为基于它的状态而改变 主体思想是将各种具体的状态类抽象出来 也就是会有很多状态类 使用场景 代码中包含大量与对象状态有关的条件语句 行为
  • C#多线程基础(一) PS:阅读C#多线程编程实战第一章总结

    一 基本概念 进程 Process 在操作系统中正在运行的应用程序被视为一个进程 包含着一个运行程序所需要的资源 进程可以包括一个或多个线程 线程 Thread 进程的基本执行单元 是操作系统分配CPU时间的基本单位 在进程入口执行的第一个
  • Git(1)

    步骤1 使用Git Bash 方法1 使用命令行进入Git安装目录的bin文件下 cd Program Files x86 Git bin 这样就可以使用Git Bash了 方法2 相比方法1更简便 步骤2 设置Git 配置email gi
  • ts类型体操 43 - Exclude

    43 Exclude by Zheeeng zheeeng easy built in union Question Implement the built in Exclude
  • Unity之Animation动画

    Unity之Animation动画 Unity之Animation绘制动画 这篇文章做最简单的动画 让一个立方体从左边移动到右边 1 创建一个Unity的新工程 名为TestAnimation 点击Create And Open按键 打开工
  • 机器学习之支持向量机: Support Vector Machines (SVM)

    机器学习之支持向量机 Support Vector Machines SVM 欢迎访问人工智能研究网 课程中心 网址是 http i youku com studyai 本篇博客介绍机器学习算法之一的支持向量机算法 理解支持向量机 Unde
  • 蓝桥杯:优秀的拆分

    蓝桥杯 优秀的拆分https www lanqiao cn problems 801 learning 目录 题目描述 输入描述 输出描述 输入输出样例 输入 输出 输入 输出 题目分析 位运算 AC代码 Java 题目描述 一般来说 一个
  • CSS font-family 中的苹方字体

    苹方提供了六个字重 font family 定义如下 苹方 简 常规体 font family PingFangSC Regular sans serif 苹方 简 极细体 font family PingFangSC Ultralight
  • 【Pytorch】六行代码实现:特征图提取与特征图可视化

    前言 之前记录过特征图的可视化 Pytorch实现特征图可视化 当时是利用IntermediateLayerGetter 实现的 但是有很大缺陷 只能获取到一级的子模块的特征图输出 无法获取内部二级子模块的输出 今天补充另一种Pytorch
  • 微信小程序API——向服务器发送请求

    在微信小程序中 我们可以通过微信的API与服务器进行数据传递 接下来我会详细介绍一下wx request的用法 首先给出前端代码 我们定义一个按钮 绑定点击事件 用于给服务器发送请求 当服务器返回数据后 通过wx for循环出数组中的数据
  • oracle如修改表字段的类型(表中有数据)

    如何在数据表有数据的情况下 修改字段类型 看到如何修改表字段类型 我想大多数人都觉得直接用修改语句 ALTER TABLE 表名 MODIFY 列名 类型 如果是修改多个字段就在后面继续 modify ALTER TABLE 表名 MODI
  • Reactive的方式访问Redis

    前言 本文主要大概介绍一下响应式 反应式编程方式访问 redis 不能解决很多生产问题 只是帮助大家对响应式编程有一个认识 本文是以Reactive 对方式访问 Redis 当然也可以访问mongodb 以及部分关系型数据库 例如 Post
  • Channel的定义、写入、读取、关闭与遍历

    目录 1 Channel基础 为什么要引入Channel Channel简介 channel的定义 声明 channel的数据类型 引用类型 向channel中写入数据 从channel中读取数据 2 channel的遍历与关闭 chann