##Day05 :
🌍并发编程🌏
goroutine :
轻量级“线程“;
非抢占式多任务处理,由协程主动交出控制权;
编译器/解释器/虚拟机层面的多任务,具体执行由调度器完成;
多个协程可以在一个或多个线程上运行;
func main(){
var a [10] int
for i := 0 ;i<10 ;i++{
go func(i int){
for{
a[i]++
//runtime.Gosched() //交出控制权,让其他goroutine有运行机会
}
}(i)
}
time.Sleep(time.Millisecond)
fmt.Println(a)
}
上面的代码简单展示了协程的工作方式,在函数前加上关键字go,表明这个函数交给main协程以外的协程由调度器执行,调度器会在合适的点进行切换。而关于协程相较于线程的轻量级,在于其主协程(main)和其他协程(…goroutine)可以相互访问。如下图:
Go语言原生支持协程,Java的一些第三方的JVM对协程支持。
goroutine可能切换的点:
I/O,select
channel
等待锁
函数调用
runtime.Gosched()
⚠️多个协程会映射到一个线程里
channel :
🍨Go语言创造者:
''不要通过共享内存来通信;通过通信来共享内存’’
import (
"fmt"
"time"
)
func worker(id int , c chan int) {
for{
n,ok := <-c
if !ok{
break
}
fmt.Printf("Worker %d receive %d\n",id,n)
}
}
func createWorker(id int)chan <- int { // <- 定义这个channel是用来发数据的
c := make(chan int)
go worker(id,c)
return c
}
func channelDemo(){
//c == nil
//channel of int channel里面的内容是int
var channels [10] chan <- int
for i := 0; i<10 ; i++{
channels [i] = createWorker(i)
}
for i:= 0 ; i<10 ;i++{
channels[i] <- 'A'+i
}
//n := <-c //收数据
//fmt.Println(s) //直接运行会报deadlock
time.Sleep(time.Millisecond)
}
func bufferedChannel(){ //只有发没有收的channel会产生死锁
n := make(chan int , 3) //设置缓冲区,不会产生deadlock
go worker(0,n)
n <- 'a'
n <- 'b'
n <- 'c'
time.Sleep(time.Millisecond)
}
func channelClose(){
n := make(chan int , 3) //设置缓冲区,不会产生deadlock
go worker(0,n)
n <- 'a'
n <- 'b'
n <- 'c'
close(n) //会收到许多空数据
time.Sleep(time.Millisecond)
}
func main(){
// channelDemo()
// bufferedChannel() //缓冲区在提升性能上是有帮助的
channelClose()
}
使用channel等待goroutine结束:
import (
"fmt"
"sync"
)
func doWork(id int , c chan int , wg *sync.WaitGroup) {
for n := range c{
fmt.Printf("Worker %d receive %c\n",id,n)
wg.Done()
}
}
type worker struct {
in chan int
wg *sync.WaitGroup
}
func createWorker(id int, wg *sync.WaitGroup) worker {
w := worker{
in: make(chan int),
wg: wg,
}
go doWork(id, w.in , wg)
return w
}
func channelDemo(){
var wg sync.WaitGroup //等待多人完成任务
var workers [10] worker
for i := 0; i<10 ; i++{
workers [i] = createWorker(i,&wg)
}
wg.Add(20)
for i, worker := range workers{
worker.in <- 'A'+i
}
for i ,worker := range workers{
worker.in <- 'a'+i
}
wg.Wait()
//wait for all of them
//time.Sleep(time.Millisecond)
}
func main(){
channelDemo()
}
使用channel实现树的遍历:
func (node *Node)TraverseWithChannel() chan *Node{ //channel里面一个一个节点去收
out := make(chan *Node)
go func() {
node.TraverseFunc(func(node *Node) {
out <- node
})
close(out)
}()
return out
}
select :
👊select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。
👊select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。
func generator() chan int{
out := make(chan int)
go func() {
i := 0
for{
time.Sleep(time.Duration(rand.Intn(1500))*
time.Millisecond)
out <- i
i++
}
}()
return out
}
func main() {
var c1, c2 = generator(),generator()
for {
select { //非阻塞
case n := <-c1:
fmt.Println("Receive from c1:", n)
case n := <-c2:
fmt.Println("Receive from c2:", n)
}
}
}
程序输出结果:
传统同步机制:
WaitGroup
Mutex (互斥量)
Cond
以Mutex为例:
type atomicInt struct {
value int
lock sync.Mutex//系统提供原子化操作,加入锁,实现原子化的int
}
func (a *atomicInt) increment(){
fmt.Println("safe increment")
func(){
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}()
}
func(a *atomicInt) get() int{
a.lock.Lock()
defer a.lock.Unlock()
return a.value
}
func main() {
var a atomicInt
a.increment()
go func() {
a.increment()
}()
time.Sleep(time.Millisecond)
fmt.Println(a.get()) //引入锁机制之后,DATA RACE没有了
}
##Day06 :
http标准库 :
⬛️使用http客户端发送请求;
⬛️使用http.Client控制请求头;
⬛️使用httputil简化工作;
func main() {
request, err := http.NewRequest(http.MethodGet,"http://www.klayhu.github.io", nil)
request.Header.Add("User-Agent","Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1")
client := http.Client{
//如果没有请求头就不需要再次重定向
CheckRedirect: func(req *http.Request,
via []*http.Request)
error {
//重定向的路径都在via里,每次的目标都在request里面,如果返回错误就会终止重定向 ---见源码
fmt.Println("Redirect:",req)
return nil
},
}
resp , err := client.Do(request)
if err != nil{
panic(err)
}
defer resp.Body.Close()
s, err := httputil.DumpResponse(resp, true)
if err != nil{
panic(err)
}
fmt.Printf("%s\n",s)
}
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!