当前位置: 首页 > wzjs >正文

那个网站教做冰鲜鱼怎么注册域名网址

那个网站教做冰鲜鱼,怎么注册域名网址,网站建设用语言,济南建设管理局官网channel channel是golang中用来实现多个goroutine通信的管道(goroutine之间的通信机制),底层是一个叫做hchan的结构体,定义在runtime包中 type hchan struct {qcount uint // 循环数组中的元素个数(通道…

channel

channel是golang中用来实现多个goroutine通信的管道(goroutine之间的通信机制),底层是一个叫做hchan的结构体,定义在runtime包中

type hchan struct {qcount   uint           // 循环数组中的元素个数(通道中元素个数)dataqsiz uint           // 循环数组的长度buf      unsafe.Pointer // 数组指针elemsize uint16          能够收发的元素的大小synctest bool // true if created in a synctest bubbleclosed   uint32        channel是否关闭的标志timer    *timer // timer feeding this chanelemtype *_type // element typesendx    uint   // 下一次发送数据的下标位置recvx    uint   // 下一次接收数据的下标位置recvq    waitq  // 读等待队列sendq    waitq  // 写等待队列// lock protects all fields in hchan, as well as several// fields in sudogs blocked on this channel.//// Do not change another G's status while holding this lock// (in particular, do not ready a G), as this can deadlock// with stack shrinking.lock mutex      //互斥锁,保证读写channel时不存在并发竞争问题
}

hchan结构体的组成部分主要有四个:

  1. buf --> 保存goroutine之间传递数据的循环链表
  2. sendx和recvx --> 记录循环链表当前发送或接收数据的下标值
  3. sendq和recvq --> 保存向chan发送和从chan接受数据的goroutine的队列
  4. lock --> 保证channel写入和读取数据时线程安全的锁

🔗有关channel的基本学习

select

select定义在runtime包中

在Linux系统下,select 是一种IO多路复用的解决方案,IO多路复用就是用一个线程处理多个IO请求。
在Golang中,select 是 在一个goroutine协程监听多个channel(代表多个goroutine)的读写事件,提高从多个channel获取信息的效率

select的使用方法与switch相似,这是一个使用select 语句的例子

select {
case <- chan1://语句1
case chan2 <- 1://语句2
default://语句3
}

对上面这段代码的解释:

第一个case:如果成功接收到chan1中的数据,执行语句1
第二个case:如果成功发送数据到chan2,执行语句2
default:如果上面case都不满足,执行语句3

select底层是一个 在runtime包中定义的 scase结构体

type scase struct {c    *hchan         // case中使用的chanelem unsafe.Pointer // 指向case包含的数据的指针
}

在case语句中(除default),包含的是对channel的读写操作,所以scase结构体中包含这两个要素:使用的channel 和指向数据的指针

select的几个规则

  1. select中的多个case的表达式必须都是channel的读写操作,不能是其他的数据类型;
  2. 如果不满足任何case语句,同时没有default,那么当前的goroutine阻塞(没有case时,所在的goroutine永久阻塞,发生panic)
  3. Go自带死锁检测机制,当发现当前协程再也没有机会被唤醒时,则发生panic
  4. select中满足多个case,随机选择一个满足的case下的语句去执行
  5. select 中只有一个case时(不是default),实际会被编译器转换为对该channel的读写操作,和实际调用data:=<-ch 或 ch<-data 没有什么区别
    例如这样的一个代码
ch := make(chan struct{})
select {
case data <- ch:fmt.Printf("ch data: %v\n", data)
}

会被编译器转换为

data := <- ch
fmt.Printf("ch data: %v\n", data)
  1. select 中有一个case + 一个 default
package main
import ("fmt"
)
func main() {ch := make(chan int)select {case ch <- 1:fmt.Println("case")default:fmt.Println("default")}
}

编译器会转换为

if selectnbsend(ch, 1) {fmt.Println("case")
} else {fmt.Println("default")
}
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {return chansend(c, elem, false, getcallerpc())
}

**runtime.selectnbsend()**函数调用runtime.chansend()函数,传入这个函数的第三个参数是false,该参数是 block,为false代表非阻塞,即每次尝试从channel读写值,如果不成功则直接返回,不会阻塞。

锁与死锁

数据竞争

多个goroutine同时对一个变量进行处理时,会造成数据竞争,某个goroutine执行的结果可能会覆盖掉其他goroutine中的操作,导致结果与预期不符
比如这样一个代码

var (x int64wg sync.WaitGroup // 等待组
)// add 对全局变量x执行5000次加1操作
func add() {for i := 0; i < 5000; i++ {x = x + 1}wg.Done()
}func main() {wg.Add(2)go add()go add()wg.Wait()fmt.Println(x)
}

预期的结果时输出10000,但是每次执行都会输出不同的结果

要想程序正确执行,可以给goroutine上锁(这个例子中是互斥锁),从而保证同一时间只有一个goroutine可以访问共享资源。

Go语言中的锁分为两种:互斥锁和读写锁

互斥锁

互斥锁只有一种锁,即sync.Mutex,是绝对锁,同一时刻一段代码只能被一个线程运行,使用方法Lock(加锁)和Unlock(解锁)即可实现

方法功能
func lock(l *mutex)获取互斥锁
func unlock(l *mutex)获取互斥锁

上面代码使用互斥锁

var (x int64wg sync.WaitGroup // 等待组m sync.Mutex // 互斥锁
)// add 对全局变量x执行5000次加1操作
func add() {for i := 0; i < 5000; i++ {m.Lock() // 修改x前加锁x = x + 1m.Unlock() // 改完解锁}wg.Done()
}func main() {wg.Add(2)go add()go add()wg.Wait()fmt.Println(x)
}

输出

10000

在Lock()和Unlock()之间的代码段称为资源的临界区(critical section),临界区的代码是要执行的代码

使用互斥锁能够保证同一时间有且只有一个 goroutine 进入临界区,其他的 goroutine 则在等待;当互斥锁释放后,等待的 goroutine 才可以获取锁进入临界区
多个 goroutine 同时等待一个锁时,唤醒的策略是随机的

读写锁

互斥锁保证了同一时间一段代码只能被一个线程运行,但是当不涉及资源的修改,只是获取资源时,使用互斥锁就没必要了,这种场景下读写锁是种更好的选择

读写锁有两种锁,即读锁和写锁。

读锁(RLock),不是绝对锁,允许多个读者同时读取;
写锁(Lock),是绝对锁,同一时刻一段代码只能被一个线程运行

当已经有读锁时,还可以任意加读锁,不可以加写锁(直到读锁全部释放)
当已经有写锁时,不可以再加读锁,也不可以再加写锁

读写锁的方法

方法功能
func (rw *RWMutex) RLock()获取读锁
func (rw *RWMutex) RUnlock()释放读锁
func (rw *RWMutex) Lock()获取写锁
func (rw *RWMutex) Unlock()释放写锁
func (rw *RWMutex) RLocker() Locker返回一个实现Locker接口的读写锁

读写锁的优点:
使用读写互斥锁在读多写少的场景下能够极大地提高程序的性能

注:对于锁而言,不应该将其作为值传递和存储,应该使用指针

死锁

当两个或两个以上的进程在执行过程中,因争夺资源而处理一种互相等待的状态,如果没有外部干涉无法继续下去,这时我们称系统处于死锁或产生了死锁

死锁主要有以下几种场景。

  1. Lock/Unlock不是成对出现时

没有成对出现容易会出现死锁的情况
例如下面只有Unlock 没有Lock 的情况

var m sync.Mutex        //锁
var wait sync.WaitGroup //等待组变量func hello() {fmt.Println("hello")
}
func main() {hello()m.Unlock()
}

报错
go fatal error: sync: unlock of unlocked mutex

使用defer ,使 lock 和unlock 紧凑出现可以增加容错

m.Lock()
defer m.Unlock()
  1. 锁被拷贝使用
func main(){m.Lock()defer m.Unlock()copyTest(m)
}func copyTest(m sync.Mutex) {  //值传递m.Lock()   //defer m.Unlock()fmt.Println("ok")
}

在函数外,加了一个Lock,在拷贝的时候又执行了一次Lock,这时候发生堵塞,而函数外层的Unlock也无法执行,所以永远获得不了这个锁,这时候就发生了死锁

  1. 交叉锁

下面这样一段代码

func main() {var mA, mB sync.Mutexvar wg sync.WaitGroupwg.Add(2)go func() {defer wg.Done()mA.Lock()defer mA.Unlock()mB.Lock()defer mB.Lock()}()go func() {defer wg.Done()mB.Lock()defer mB.Lock()mA.Lock()defer mA.Unlock()}()wg.Wait()
}

执行后
go fatal error: all goroutines are asleep - deadlock!

执行过程:
goroutine1获取mA
goroutine2获取mB
goroutine1尝试获取mB,但是已经被goroutine2获取,等待mB释放
goroutine2尝试获取mA,但是已经被goroutine1获取,等待mA释放
两者都在等对方释放锁,形成死锁

http://www.dtcms.com/wzjs/25311.html

相关文章:

  • 做网站组织结构框架例子腰肌劳损的自我治疗和恢复的方法有什么?
  • 网站换域名seo怎么做知名网页设计公司
  • 国外有哪些网站做推广的比较好国家最新新闻
  • 南昌优化网站分析关键词优化骗局
  • 购物网站建设代码特色产品推广方案
  • 广东建设信息网三类人站长之家seo信息
  • 管家网站关键词优化公司如何选择
  • 网站搜索引擎拓客google官网入口
  • 网站建设价格标准青岛seo培训
  • wordpress主题去除友情链接seo怎么优化网站排名
  • php网站开发注意问题专业网站建设公司
  • 上海网站建设网页制网页入口网站推广
  • 不要钱的ppt模板网站北京营销推广网站建设
  • 网站建设费 广告武安百度seo
  • 规划设计公司年终总结在线看seo网站
  • 培训机构倒闭seo排名工具给您好的建议下载官网
  • 电脑做ppt模板下载网站某产品网络营销推广方案
  • 椒江建设工程机械厂网站品牌整合营销
  • 做网站项目主要技术seo资讯推推蛙
  • 网站设计需要多久怎样上百度做广告
  • 专业营销的网站建设公司排名百度推广公司怎么代理到的
  • 专业的大良网站设计俄罗斯搜索引擎浏览器官网入口
  • 南京网站建设网站设计seo中介平台
  • 备案后可以修改网站吗网站生成app工具
  • 网页设计师的要求结构优化设计
  • 浙江电商网站建设销售万能搜索引擎
  • 做司考题的网站松原今日头条新闻
  • 千助网站公司企业如何做网站
  • 上海本市企业查询一键优化软件
  • 网站独立ip百度收录网络推广怎么样