等待组 WaitGroup
<p>本节目标</p>
<h2>sync同步包,是Go语言提供的内置同步操作,保证数据统一的一些方法</h2>
<h1>等待组</h1>
<p>WaitGroup 等待一个goroutine的集合执行完成,也叫同步等待组
使用<code>Add()</code>方法,来设置要等待一组goroutine 要执行的数量。
用<code>Done()</code>方法来减去执行goroutine集合的数量。
使用<code>Wait()</code> 方法让<code>主goroutine</code>
也就是<code>main</code>函数进入阻塞状态,等待其他的子goroutine执行结束后,main函数才会解除阻塞状态。</p>
<h3>没使用等待组</h3>
<pre><code class="language-go">var num int
func main() {
//go BuyTicket()
//go BuyTicket()
//go BuyTicket()
for i := 0; i &lt; 3; i++ {
go BuyTicket()
}
// time.Sleep() 是一个简单的休眠函数,调用后会暂停当前协程的执行一段时间,再继续执行下面的代码
time.Sleep(5 * time.Second)
fmt.Println(num)
}
func BuyTicket() {
for i := 1; i &lt;= 10000; i++ {
num++
}
}</code></pre>
<h3>使用后</h3>
<pre><code class="language-go">var num int // 总票数
var wg sync.WaitGroup // 等待组
func main() {
var count = 3 //并发数
//wg.Add(count)
//go BuyTicket()
//go BuyTicket()
//go BuyTicket()
//wg.Wait()
for i := 0; i &lt; count; i++ {
wg.Add(1)
go BuyTicket()
}
wg.Wait()
//time.Sleep(5 * time.Second)
fmt.Println(num)
}
func BuyTicket() {
defer wg.Done()
for i := 1; i &lt;= 10000; i++ {
num++
}
}
</code></pre>
<p>>等待组(WaitGroup)和 time.Sleep() 都可以实现协程等待的效果,但它们的实现方式和适用场景有所不同。
time.Sleep() 是一个简单的休眠函数,调用后会暂停当前协程的执行一段时间,再继续执行下面的代码
而等待组则是一种更加灵活和高效的协程管理方式,它能够实现多个协程的同步和等待。</p>
<p><font color='red'>在多协程并发抢票的情况下,可能存在线程安全问题,导致最终卖出总票数不是预期的值</font>。具体来说,可能会出现以下两种情况:</p>
<ol>
<li>数据竞争问题</li>
</ol>
<p>如果三个协程同时对同一个变量进行写操作(例如,修改共享变量表示剩余票数),就可能出现数据竞争问题。根据调度器的具体执行顺序,可能会出现某些协程同时写入相同的值或者覆盖彼此的结果,从而导致最终卖出总票数不是预期的值。</p>
<ol>
<li>重复售票问题</li>
</ol>
<p>在多协程并发抢票的情况下,可能会出现重复售票的问题。如果没有合理的同步机制,可能会导致一个票被多个协程抢到并被多次售出,从而导致最终卖出总票数不是预期的值。</p>
<p>为了解决这些问题,可以考虑使用互斥锁、通道(Channel)等同步机制,以保证协程之间的正确性和一致性。例如,在卖票过程中可以使用互斥锁来保证每个协程对剩余票数的修改是线程安全的,避免数据竞争问题;同时,可以使用通道来防止重复售票的问题,每个协程通过通道获取一个唯一的票号来进行售票,避免重复卖出同一张票。</p>
<p>总之,要保证多协程并发抢票时协程之间的正确性和一致性,需要采用合适的同步机制来避免数据竞争和重复售票等问题。</p>