golang支持逻辑上处理多个任务即并发,golang中的coroute协程的支持是优秀的,其本质上利用了多线程和协程配合提高处理器时间片的利用率。使用go可以创建一个并发单元,使用WaitGroup可以让主线程等待并发单元执行完再推出。关于golang中并发的详细设计可以参考:https://blog.csdn.net/wjb123sw99/article/details/105413639
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1) // 计数器+1
j := i
go func() { // 开启并发单元
defer wg.Done() // 计数器-1
fmt.Println("Corunte", j, &j, &i)
}()
}
fmt.Println("wg.Wait()")
wg.Wait() // 等待并发单元执行完
fmt.Println("exit")
}
golang未实现严格的并发安全,然而每个并发单元共享全局变量、同域的局部变量。因此建议使用CSP通道,以通信代替内存共享,实现并发安全。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
cn := make(chan string) // 创建一个通道
wg.Add(1)
go func() { // 开启并发单元
defer wg.Done() // 计数器-1
fmt.Println(<-cn) // 读取通道内容
}()
cn <- "hello world" // x写数据到通道中
wg.Wait() // 等待并发单元执行完
}
同步模式的通道,必须有配对的读写,否则会阻塞。异步的通道在缓存区未满或者未读取完前不会阻塞。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
cn := make(chan string) // 创建一个通道,同步
cn2 := make(chan string, 2) // 创建一个通道,异步,带有2个缓存槽
wg.Add(1)
go func() { // 开启并发单元
defer wg.Done() // 计数器-1
time.Sleep(time.Second)
fmt.Println(<-cn) // 读取通道内容
close(cn) // 关闭通道
}()
cn <- "hello" // x写数据到通道中, 同步在通道未读取前会阻塞
fmt.Println("main")
wg.Add(1)
go func() { // 开启并发单元
defer wg.Done() // 计数器-1
time.Sleep(time.Second)
fmt.Println(<-cn2) // 读取通道内容
close(cn2) // 关闭通道
}()
cn2 <- "hello" // x写数据到通道中, 异步不会阻塞
fmt.Println("main")
wg.Wait() // 等待并发单元执行完
}
可以使用cap返回通道缓存区槽数,使用len返回通道当前已缓存数量。
package main
import (
"fmt"
)
func main() {
cn := make(chan string) // 创建一个通道,同步
cn2 := make(chan string, 2) // 创建一个通道,异步,带有2个缓存槽
cn3 := make(chan string, 4) // 创建一个通道,异步,带有4个缓存槽
cn3 <- "hello"
cn3 <- "world"
fmt.Println(cap(cn), len(cn))
fmt.Println(cap(cn2), len(cn2))
fmt.Println(cap(cn3), len(cn3))
}
使用ok-idom或者range来实现简单数据收发
package main
import "fmt"
func test_ok() {
x := make(chan int)
go func() {
for {
i, ok := <-x // 读取数据直到通道关闭
if !ok {
return
}
fmt.Println(i)
}
}()
x <- 1
x <- 2
x <- 3
close(x)
}
func test_range() {
x := make(chan int)
go func() {
for i := range x { // 读取数据直到通道关闭
fmt.Println(i)
}
}()
x <- 1
x <- 2
x <- 3
close(x)
}
func main() {
test_ok()
test_range()
}
golang支持将通道的收发权限分别赋予不同的变量,来实现单向通道。需要注意的是,只有发送权限的一方可以关闭通道。
package main
import "fmt"
func main() {
a := make(chan int, 2)
var recv <-chan int = a
var send chan<- int = a
send <- 1 // 单向发送
// recv <- 1 invalid operation: recv <- 1 (send to receive-only type <-chan int)
close(send)
fmt.Println(<-recv) // 单向接收
}
使用过程中,往往将goroutine和通道绑定,实现工厂模式。
package main
import (
"fmt"
"sync"
)
type receiver struct {
sync.WaitGroup
data chan int
}
func test() *receiver {
r := &receiver{
data: make(chan int), // 单缓存通道
}
r.Add(1)
go func() {
defer r.Done()
for x := range r.data {
fmt.Println("recv:", x)
}
}()
return r
}
func main() {
a := test()
a.data <- 1
a.data <- 2
close(a.data)
a.Wait()
}
golang反射机制(reflect)能让我们在程序运行期间探知对象的数据类型和内存结构.
func TypeOf(i interface) Type // 查看接口数据类型
func ValueOf(i interface) Value //查看值
在反射机制中,获取数据类型需要区分静态类型和基础类型。
package main
import (
"fmt"
"reflect"
)
type X int
type Y int
func main() {
var a X = 1
var b Y = 2
at := reflect.TypeOf(a)
av := reflect.ValueOf(a)
bt := reflect.TypeOf(b)
bv := reflect.ValueOf(b)
fmt.Println(av, bv)
fmt.Println(at.Kind(), bt.Kind()) // 基础类型
fmt.Println(at.Name(), bt.Name()) // 静态类型
fmt.Println()
}