Go语言设计与实现 -- WaitGroup, Once, Cond

WaitGroup

我们可以通过 sync.WaitGroup 将原本顺序执行的代码在多个 Goroutine 中并发执行,加快程序处理的速度。

golang-syncgroup

我们来看一下sync.WaitGroup的结构体:

type WaitGroup struct {
    //保证WaitGroup不会被开发者通过再赋值的方式复制
	noCopy noCopy

	// 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
	// 64-bit atomic operations require 64-bit alignment, but 32-bit
	// compilers only guarantee that 64-bit fields are 32-bit aligned.
	// For this reason on 32 bit architectures we need to check in state()
	// if state1 is aligned or not, and dynamically "swap" the field order if
	// needed.
	state1 uint64
	state2 uint32
}

golang-waitgroup-state

counterWaitGroup里面需要等待的Goroutine的数量

waitersema是信号量。

在分析源码之后我们得到以下结论:

  • sync.WaitGroup必须在Wait方法返回之后才能重新使用

  • Done方法只是对Add的简单封装。

    // Done decrements the WaitGroup counter by one.
    func (wg *WaitGroup) Done() {
       wg.Add(-1)
    }
    

    我们可以向Add方法传入任意负数,快速将计数器归零以唤醒等待的Goroutine。但是需要注意的是:计数器非负,如果计数器是负数,那么程序就会直接崩溃

  • 可以有多个Goroutine等待当前计数器归零,这些Goroutine会被同时唤醒

Once

sync.Once可以保证Go语言程序运行期间某段代码只会执行一次。

func main() {
   o := &sync.Once{}
   for i := 0; i < 10; i++ {
      o.Do(func() {
         fmt.Println("I love zyq!!")
      })
   }
}

// I love zyq!!

我们来看一下它的结构体:=

type Once struct {
   // done indicates whether the action has been performed.
   // It is first in the struct because it is used in the hot path.
   // The hot path is inlined at every call site.
   // Placing done first allows more compact instructions on some architectures (amd64/386),
   // and fewer instructions (to calculate offset) on other architectures.
   done uint32
   m    Mutex
}

会通过done确保函数不会执行第二次

再来看看Do方法:

func (o *Once) Do(f func()) {
   // Note: Here is an incorrect implementation of Do:
   //
   // if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
   //    f()
   // }
   //
   // Do guarantees that when it returns, f has finished.
   // This implementation would not implement that guarantee:
   // given two simultaneous calls, the winner of the cas would
   // call f, and the second would return immediately, without
   // waiting for the first's call to f to complete.
   // This is why the slow path falls back to a mutex, and why
   // the atomic.StoreUint32 must be delayed until after f returns.

   if atomic.LoadUint32(&o.done) == 0 {
      // Outlined slow-path to allow inlining of the fast-path.
      o.doSlow(f)
   }
}

通过done这个成员变量,我们可以达到以下的执行效果

  • 如果传入的函数已经执行过,会直接返回
  • 如果没有执行过,也就是o.done还是0,那么就会调用 o.doSlow(f)执行传入的函数。
func (o *Once) doSlow(f func()) {
   o.m.Lock()
   defer o.m.Unlock()
   if o.done == 0 {
      defer atomic.StoreUint32(&o.done, 1)
      f()
   }
}

这个函数的逻辑如下:

  • 为当前Goroutine获取互斥锁
  • 执行传入的无入参函数
  • 运行延迟调用,将成员变量done的值更新为1

由源码我们可以知道一个使用的注意事项:

这个方法传入不同的函数只会执行第一次哦调用传入的函数

Cond

我们先来看一下它的用法:

var status int64

func main() {
   c := sync.NewCond(&sync.Mutex{})
   for i := 0; i < 10; i++ {
      go listen(c)
   }
   time.Sleep(1 * time.Second)
   go broadcast(c)

   ch := make(chan os.Signal, 1)
   signal.Notify(ch, os.Interrupt)
   <-ch
}

func listen(c *sync.Cond) {
   c.L.Lock()
   for atomic.LoadInt64(&status) != 1 {
      c.Wait()
   }
   fmt.Println("listen")
   c.L.Unlock()
}

func broadcast(c *sync.Cond) {
   c.L.Lock()
   atomic.StoreInt64(&status, 1)
   c.Broadcast()
   c.L.Unlock()
}

该程序一共运行了11个Goroutine

  • 10个通过sync.Cond.Wait等待特定条件满足
  • 1个会调用sync.Cond.Broadcast唤醒所有陷入等待的Gourtine

上述代码会打印出10个listen

golang-cond-broadcast

上面出现了这个代码,所以我们也顺便介绍一下:

func Notify(c chan<- os.Signal, sig ...os.Signal) 

Notify函数让signal包将输入信号转发到c。如果没有列出要传递的信号,会将所有输入信号传递到c;否则只传递列出的输入信号。

结构体

type Cond struct {
   noCopy noCopy

   // L is held while observing or changing the condition
   L Locker

   notify  notifyList
   checker copyChecker
}
  • noCopy保证结构体不会在编译期间复制
  • L用户保护内部的notify字段,Locker接口类型的变量
  • notify是一个Goroutine的链表,它是实现同步机制的核心结构
  • copyChecker用于禁止运行期间发生的复制
type notifyList struct {
	wait uint32
	notify uint32

	lock mutex
	head *sudog
	tail *sudog
}

sync.notifyList 结构体中,headtail 分别指向的链表的头和尾,waitnotify 分别表示当前正在等待的和已经通知到的 Goroutine 的索引。

### 回答1: evap-cond设计软件是一种用于蒸发冷凝器设计和计算的软件工具,用户可以通过该软件进行蒸发冷凝器的参数选择、传热计算、性能评估和优化设计等工作。 要下载evap-cond设计软件,可以通过以下步骤进行: 1. 在互联网上搜索evap-cond设计软件的官方网站或可信赖的软件下载网站。 2. 找到软件的下载页面,查看软件的版本和兼容性要求。确保选择与操作系统和计算机硬件兼容的版本。 3. 点击下载链接或按钮,开始下载软件的安装文件。下载文件的格式可能是常见的可执行文件(.exe)或压缩文件(.zip)。如果是压缩文件,需要解压缩后再进行安装。 4. 下载完成后,双击安装文件,按照提示进行软件的安装。在安装过程中,可以选择安装路径和其他相关选项。 5. 完成安装后,可以在计算机的开始菜单或桌面上找到软件的快捷方式。双击快捷方式,启动软件。 6. 在软件界面中,根据需求和设计要求,输入相关的参数和条件,进行蒸发冷凝器的设计和计算。 evap-cond设计软件具有直观的界面和丰富的功能,可帮助工程师和设计师完成蒸发冷凝器的设计和优化工作,从而提高系统的性能和效率。下载并使用这种软件可以减少设计时间和成本,提高工作效率和设计准确性。 ### 回答2: 要下载evap-cond设计软件,首先需要进入相关的软件下载网站,例如常用的软件分享网站或软件官方网站。在网站的搜索栏中输入"evap-cond设计软件"进行搜索,然后会显示相关的下载链接。 在选择下载链接之前,可以先查看软件的描述、版本号、更新日志等信息,确保所下载的软件版本适用于自己的操作系统。一般来说,软件官方网站提供的下载链接更加可靠,可以优先考虑。 点击合适的下载链接后,会弹出下载提示框,选择保存文件的位置,并点击“保存”进行下载。下载的速度会根据网络状况和文件大小而有所不同,耐心等待下载完成。 下载完成后,进入保存文件的位置,找到刚刚下载的软件文件,通常是一个安装包(.exe或.msi格式)。双击该文件,开始安装过程。 安装过程中,根据软件的指示完成安装步骤,通常包括选择安装位置、同意软件协议、选择开始菜单文件夹等选项。点击“下一步”或“安装”等按钮,等待安装过程完成。 安装完成后,即可在开始菜单或桌面上找到该软件的图标,双击打开软件。根据软件的界面和操作指引,开始使用evap-cond设计软件进行相关设计工作。 需要注意的是,下载和安装软件时要保证电脑安全,不要下载来路不明的软件,以免损害系统安全。另外,最好保持软件的更新,以获取最新的功能和修复bug。 ### 回答3: 要下载evap-cond设计软件,首先需要确定你要下载的软件具体是哪个版本,是否有免费试用的选项。接下来,可以按照以下步骤进行软件下载: 1. 在计算机上打开一个浏览器,比如Google Chrome或者火狐浏览器。 2. 输入“evap-cond设计软件下载”或者具体的软件名称,点击搜索按钮。 3. 在搜索结果中找到官方网站或可靠的第三方软件下载网站。 4. 进入官方网站或者下载网站,并寻找与软件相关的下载选项或页面。 5. 如果官方网站提供了软件试用版本,可以直接点击试用下载按钮。如果没有提供试用版,需要购买软件或查看其他下载选项。 6. 如果是第三方下载网站,要确保所下载的软件版本是官方认可的,并且该网站有良好的信誉度和用户评价。 7. 点击下载按钮,等待软件下载完成。下载过程的时间可能取决于软件的大小和你的网络速度。 8. 下载完成后,可以在计算机的下载文件夹或指定下载位置找到所下载的软件安装包。 9. 双击安装包,按照安装向导的步骤进行软件安装。通常要阅读并同意软件许可协议,选择安装路径和相关设置。 10. 完成安装后,可以在计算机的开始菜单或桌面上找到该软件的快捷方式,打开软件并开始使用。 请注意,在下载和安装任何软件之前,要确保计算机的安全性和稳定性。最好使用官方或信誉度高的第三方网站进行下载,并确保软件是来源可靠的,以避免安全风险和恶意软件的存在。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡桃姓胡,蝴蝶也姓胡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值