注:本文所有函数名为中文名,并不符合代码规范,仅供读者理解参考。
Go程不是OS线程,也不是绿色线程(语言运行时管理的线程),而是更高级别的抽象,一种特殊的协程。是一种非抢占式的简单并发子goroutine(函数、闭包、方法)。不能被中断,但有多个point可以暂停或重新进入。
goroutine 在它们所创建的相同地址空间内执行,特别是在循环创建go程的时候,推荐将变量显式映射到闭包(引用外部作用域变量的函数)中。

Fork 在程序中的任意节点,子节支可以与父节点同时运行。join 在将来某个时候这些并发分支会合并在一起,这是保持程序正确性和消除竞争条件的关键。Go语言遵循 fork-join并发模型。
使用 go func 其实就是在创建 fork point,为了创建 join point,我们需要解决竞争条件。
func 竞争条件_解决() {
var wg sync.WaitGroup
var data int
wg.Add(1)
go func() {
defer wg.Done()
data++
}()
wg.Wait()
if data == 0 {
fmt.Println("Value", data)
} else {
fmt.Println("Value 不是 0")
}
}
通过 sync.WaitGroup 我们阻塞 main 直到 go 程退出后再让 main 继续执行,实现了 join point。可以理解为并发-安全计数器,经常配合循环使用。
这是一个同步访问共享内存的例子。使用前提是你不关心并发操作的结果,或者你有其他方法来收集它们的结果。
wg.Add(1) 是在帮助跟踪的goroutine之外完成的,如果放在匿名函数内部,会产生竞争条件。因为你不知道go程什么时候被调度。
type state struct {
lock sync.Mutex
count int
}
func 结构体修改状态_互斥锁() {
s := state{}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
// s.lock.Lock()
defer wg.Done()
// defer s.lock.Unlock()
s.count++
}()
}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
// s.lock.Lock()
defer wg.Done()
// defer s.lock.Unlock()
s.count--
}()
}
wg.Wait()
fmt.Println(s.count)
}
没有互斥锁的时候,会导致发生竞争现象,取消互斥锁的注释,最终结果为理想的0。
进入和退出一个临界区是有消耗的,所以一般人会尽量减少在临界区的时间。
本质和普通的互斥锁相同,但是可以保证在未锁的情况允许多个读消费者持有一个读锁,在读消费者非常多的情况下可以提高性能。
在多个读消费者的情况下,通常使用 RWMutex ,读消费者较少时,Mutex和RWMutex两者都可用。
cond : 一个goroutine的集合点,等待或发布一个event。
多个go程暂停在某个point上,等待一个事件信号再继续执行。没有cond的时候是怎么做的,当然是for循环,但是这有个大问题。
func 无cond() {
isOK := false
go func() {
for isOK == false {
// time.Sleep(time.Microsecond) // bad method
// do something
}
fmt.Println("OK I finished")
}()
go func() {
for isOK == false {
// time.Sleep(time.Microsecond) // bad method
// do something
}
fmt.Println("OK I finished")
}()
time.Sleep(time.Second * 5)
isOK = true
select {}
}

这会消耗一整个CPU核心的所有周期,有些人会引入 time.Sleep 实际上这会让算法低效,这时候我们可以使用 cond。
func 有cond() {
var wg sync.WaitGroup
cond := sync.NewCond(&sync.Mutex{})
test := func() {
defer wg.Done()
defer cond.L.Unlock()
cond.L.Lock()
cond.Wait()
fmt.Println("something work...OK finished")
}
wg.Add(2)
go test()
go test()
time.Sleep(time.Second * 5)
cond.Broadcast() // 通知所有go程
// cond.Signal() // 通知等待时间最久的一个go程
wg.Wait()
}
cond运行时内部维护一个FIFO列表。与利用channel相比,cond类型性能要高很多。
可以配合单例模式使用,将判断对象是否为null改为sync.Once用于创建唯一对象。
sync.Once只计算调用Do方法的次数,而不是多少次唯一调用Do方法。所以在必要情况下声明多个sync.Once变量而不是用一个。下面的例子输出 1
func 只调用一次() {
var once sync.Once
count := 0
once.Do(func() {
count++
})
once.Do(func() {
count--
})
fmt.Println(count)
}
对象池模式是一种创建和提供可供使用的固定数量实例或Pool实例的方法。通常用于约束创建昂贵的场景,比如数据库连接,以便只创建固定数量的实例,但不确定数量的操作仍然可以请求访问这些场景。
使用pool的另一个原因是实例化的对象会被GC自动清理,而pool不会
你的并发进程需要请求一个对象,但是在实例化之后很快地处理它们,或者在这些对象的构造可能会对内存产生负面影响,这时最好使用Pool设计模式。但是必须确保pool中对象是同质的,否则性能大打折扣。
注意事项
type conn struct{}
func 对象池() {
pool := &sync.Pool{New: func() any {
time.Sleep(time.Millisecond * 250)
fmt.Println("创建连接对象")
return &conn{}
}}
for i := 0; i < 10; i++ {
pool.Put(pool.New())
}
fmt.Println("初始化结束")
c1 := pool.Get()
c2 := pool.Get()
pool.Put(c1)
pool.Put(c2)
}
channel也可以用来同步内存访问,但最好用于在goroutine之间传递消息(channel是将goroutine绑定在一起的粘合剂)。双向 chan 变量名后缀加 Stream
带缓存的channel和不带缓存的channel声明是一样的
var dataStream chan interface{}
双向channel可以隐式转换成单向channel,这对函数返回单向通道很有用
var receiveChan <-chan interface{}
var sendChan chan<- interface{}
dataStream := make(chan interface{})
receiveChan = dataStream
sendChan = datraStream
go语言中channel是阻塞的,意味着channel内的数据被消费后,新的数据才可以写入。通过 <- 操作符的接受形式可以选择返回两个值。
salutation,ok := <-dataStream
当channel未关闭时,ok返回true,关闭后返回false。即使channel关闭了,也能读取到默认值,为了支持一个channel有单个上游写入,有多个下游读取。
模拟之前WaitGroup的例子
func 竞争条件_通道() {
var data int
var Stream chan interface{} = make(chan interface{})
go func() {
data++
Stream <- struct{}{}
}()
<-Stream
if data == 0 {
fmt.Println("Value", data)
} else {
fmt.Println("Value 不是 0")
}
}
模拟之前cond同步多个go程的例子
func channel代替cond() {
var wg sync.WaitGroup
Stream := make(chan interface{})
test := func() {
defer wg.Done()
<-Stream
fmt.Println("something work...OK finished")
}
wg.Add(1)
go test()
go test()
time.Sleep(time.Second * 5)
close(Stream)
wg.Wait()
}
在同一时间打开或关闭多个goroutine可以考虑用channel。
channel操作结果
| 操作 | Channel状态 | 结果 |
|---|---|---|
| Read | nil | 阻塞 |
| 打开且非空 | 输出值 | |
| 打开但空 | 阻塞 | |
| 关闭的 | 默认值,false | |
| 只写 | 编译错误 | |
| Write | nil | 阻塞 |
| 打开但填满 | 阻塞 | |
| 打开但不满 | 写入 | |
| 关闭的 | panic | |
| 只读 | 编译错误 | |
| close | nil | panic |
| 打开且非空 | 关闭Channel;仍然能读取通道数据,直到读取完毕返回默认值 | |
| 打开但空 | 关闭Channel;返回默认值 | |
| 关闭的 | panic | |
| 只读 | 编译错误 |
在正确的环境中配置Channel,分配channel的所有权。这里的所有权被定义为 实例化、写入和关闭channel的goroutine。重要的是弄清楚哪个goroutine拥有channel。
单向channel声明的是一种工具,允许我们区分所有者和使用者。一旦我们将channel所有者和非channel所有者区分开来,前面的表的结果会非常清晰。可以开始讲责任分配给哪些拥有channel的goroutine和不拥有channel的goroutine。
拥有channel的goroutine
使用channel的goroutine
尽量保持channel的所有权很小,消费者函数只能执行channel的读取方法,因此只需要知道它应该如何处理阻塞和channel的关闭。
func 通道使用哲学() {
// 所有权范围足够小,职责明确
chanOwner := func() <-chan int {
resultStream := make(chan int, 5)
go func() {
defer close(resultStream)
for i := 0; i < 5; i++ {
resultStream <- i
}
}()
return resultStream // 传递单向通道给另一个 goroutine
}
resultStream := chanOwner()
for result := range resultStream {
fmt.Println(result)
}
fmt.Println("Done")
}
Go语言运行时将在一组case语句中执行伪随机选择。
var c<-chan int // 注意是 nil,永远阻塞
select{
case <-c:
case <- time.After(1 * time.Second):
fmt.Println("Timed out.")
}
time.After函数通过传入time.Duration参数返回一个数值并写入channel。select允许加default语句,通常配合for-select循环一起使用,允许go程在等待另一个go程结果的同时,自己干一些事情。
通过修改 runtime.GOMAXPROCS 允许你修改OS线程的数量。一般是为了调试,添加OS线程来更频繁触发竞争条件。
《Go语言并发之道》Katherine CoxBuday
《Go语言核心编程》李文塔
《Go语言高级编程》柴树彬、曹春辉
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------
//1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
1.在Python3中,下列关于数学运算结果正确的是:(B)a=10b=3print(a//b)print(a%b)print(a/b)A.3,3,3.3333...B.3,1,3.3333...C.3.3333...,3.3333...,3D.3.3333...,1,3.3333...解析: 在Python中,//表示地板除(向下取整),%表示取余,/表示除(Python2向下取整返回3)2.如下程序Python2会打印多少个数:(D)k=1000whilek>1: print(k)k=k/2A.1000 B.10C.11D.9解析: 按照题意每次循环K/2,直到K值小于等
Rails相对较新。我正在尝试调用一个API,它应该向我返回一个唯一的URL。我的应用程序中捆绑了HTTParty。我已经创建了一个UniqueNumberController,并且我已经阅读了几个HTTParty指南,直到我想要什么,但也许我只是有点迷路,真的不知道该怎么做。基本上,我需要做的就是调用API,获取它返回的URL,然后将该URL插入到用户的数据库中。谁能给我指出正确的方向或与我分享一些代码? 最佳答案 假设API为JSON格式并返回如下数据:{"url":"http://example.com/unique-url"
我正在开发我的第一个Rubygem,并捆绑了cucumber、rspec和shoulda-matches进行测试。当我运行rspec时,出现以下错误:/app/my_gem/spec/spec_helper.rb:6:in`':undefinedmethod`configure'forShoulda::Matchers:Module(NoMethodError)这是我的gem规范:#my_gem.gemspec...Gem::Specification.newdo|spec|......spec.add_development_dependency"activemodel"spec.a