go语言:channel作为锁

2021-04-21  本文已影响0人  lifefruity

在看https://github.com/GuoZhaoran/spikeSystem案例的时候发现有用channel作为锁的功能,看了下不太明白,所以自己写了个小例子,能成功模拟出锁的功效,成功的例子,使用ab模拟高并发,貌似是串行的,因为能明显感受到请求baidu的100个链接

并发命令:ab -n 100 -c 100 192.168.1.53:3005/sayHi

package main

import (
    "fmt"
    "net/http"
    "log"

)

var (
    done chan int
)

func init() {
    done = make(chan int, 1)
    done <- 1
}

var globalnum = 1

//为什么 https://github.com/GuoZhaoran/spikeSystem 里需要 done <- 1 这样的,但是去掉又不对。但是在这个例子中又不会有问题

func sayHi(w http.ResponseWriter, r *http.Request)  {
    x := <-done
    fmt.Println(globalnum) //在终端打印的
    fmt.Fprint(w, x) //页面上打印的
    
    rsp , _:= http.Get("http://www.baidu.com")
    _ = rsp
    
    globalnum += 1
    
    done <- 1
}

func main() {
    http.HandleFunc("/sayHi", sayHi)
    log.Fatal(http.ListenAndServe("0.0.0.0:3005", nil))
}
  1. 错误1
    如果把网络channel去掉,像下面的代码,则打印的globalnum就是错的了,次数类似打印1个1和99个2,其实在并发100个的时候,就请求了2次baidu
func sayHi(w http.ResponseWriter, r *http.Request)  {
    //x := <-done
    fmt.Println(globalnum) //在终端打印的
    //fmt.Fprint(w, x) //页面上打印的
    
    rsp , _:= http.Get("http://www.baidu.com")
    _ = rsp
    
    globalnum += 1
    
    //done <- 1
}
  1. 在#1的基础上,不请求网络,也就是不需要耗时的情况时,是打印1~100
func sayHi(w http.ResponseWriter, r *http.Request)  {
    //x := <-done
    fmt.Println(globalnum) //在终端打印的
    //fmt.Fprint(w, x) //页面上打印的
    
    //rsp , _:= http.Get("http://www.baidu.com")
    //_ = rsp
    
    globalnum += 1
    
    //done <- 1
}
  1. 这样写也是可以的
package main

import (
    "fmt"
    "net/http"
    "log"

)

var (
    done chan int
)

func init() {
    done = make(chan int, 1)
    //done <- 1
}

var globalnum = 1

//为什么 https://github.com/GuoZhaoran/spikeSystem 里需要 done <- 1 这样的,但是去掉又不对。但是在这个例子中又不会有问题

func sayHi(w http.ResponseWriter, r *http.Request)  {
    
    done <- 1
    fmt.Println(globalnum) //在终端打印的
    
    
    rsp , _:= http.Get("http://www.baidu.com")
    _ = rsp
    
    globalnum += 1
    
    x := <-done
    fmt.Fprint(w, x) //页面上打印的
}

func main() {
    http.HandleFunc("/sayHi", sayHi)
    log.Fatal(http.ListenAndServe("0.0.0.0:3005", nil))
}

不太明白其中的原理为什么会出现,参考这2篇文章 (onedrive\go channel 001目录)
https://www.cnblogs.com/wongbingming/p/13035179.html
https://studygolang.com/articles/6024

主要是因为如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。

怎么理解上面那句话呢,就是缓冲区里满了,下次在写入会被阻塞。比如如下代码,缓冲区满了,后面的第二次就被阻塞了,第一次的接受方一直没获取,导致代码被阻塞了(接收方在有值可以接收之前会一直阻塞)。

func sayHi(w http.ResponseWriter, r *http.Request)  {
    done <- 1
    fmt.Println(globalnum) //在终端打印的
    
    rsp , _:= http.Get("http://www.baidu.com")
    _ = rsp
    
    globalnum += 1
    
    //x := <-done
    fmt.Fprint(w, 1) //页面上打印的
}
上一篇下一篇

猜你喜欢

热点阅读