GO语言学习笔记(五)并发、通道、反射

并发

  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")
}

GO语言学习笔记(五)并发、通道、反射_第1张图片

通道

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()
}

GO语言学习笔记(五)并发、通道、反射_第2张图片

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()
}

 

 

 

 

 

你可能感兴趣的:(GO)