Go-共享内存并发机制

目录

一、并发机制

1、非线程安全

2、线程安全

二、WaitGroup - 同步等待协程执行结果

三、总结


一、并发机制

1、非线程安全

package share_mem

import (
	"fmt"
	"sync"
	"testing"
	"time"
)

//线程不安全计数器
func TestCounter(t *testing.T)  {
	var counter int

	for i := 0; i < 5000; i++ {
		go func() {
			counter++
		}()
	}

	//这里等一秒是因为我们不知道所有的协程执行完毕需要多久,
	//防止主程序已经退出了,协程还没执行完导致结果不准确,后面会介绍更好的处理方案。
	time.Sleep(time.Second * 1)
	fmt.Printf("counter=%d\n", counter)
}

/*
可以看到下面的执行结果,counter 并不等于 5000

=== RUN   TestCounter
counter=4775
--- PASS: TestCounter (1.01s)
PASS
*/

当我们不使用锁的机制来使用协程时,counter 变量在不同的协程里面去做自增,导致了并发时锁的竞争,丢失了很多正确的写操作,即不是一个线程安全的程序。

2、线程安全

那么我们现在就要对这个共享内存做一个线程保护

package share_mem

import (
	"fmt"
	"sync"
	"testing"
	"time"
)


//线程安全计数器
func TestCounterThreadSafe(t *testing.T)  {
	var mut sync.Mutex
	var counter int

	for i := 0; i < 5000; i++ {
		go func() {
			//锁的释放我们一般都要写在 defer 里面,
			//是为了防止某一些未知异常被触发时,
			//忘了将锁释放锁而导致程序整体被挂起
			defer func() {
				mut.Unlock()
			}()
			//共享内存加锁
			mut.Lock()
			counter++
		}()
	}

	//这里等一秒是因为我们不知道所有的协程执行完毕需要多久,
	//防止主程序已经退出了,协程还没执行完导致结果不准确,后面会介绍更好的处理方案。
	time.Sleep(time.Second * 1)
	fmt.Printf("counter=%d\n", counter)
}

/*
可以看到下面的执行结果,counter 等于 5000

=== RUN   TestCounterThreadSafe
counter=5000
--- PASS: TestCounterThreadSafe (1.01s)
PASS
*/

二、WaitGroup - 同步等待协程执行结果

WaitGroup 的作用就是只有 Wait 到所有协程里面的东西都执行完毕之后,程序才能继续往下执行,

package share_mem

import (
	"fmt"
	"sync"
	"testing"
	"time"
)


//WaitGroup 优化版线程安全计数器
func TestCounterWaitGroup(t *testing.T)  {
	var mut sync.Mutex
	var wg sync.WaitGroup
	var counter int

	for i := 0; i < 5000; i++ {
		//每启动一个协程,我们的等待数量就要加一
		wg.Add(1 )
		go func() {
			//锁的释放我们一般都要写在 defer 里面,
			//是为了防止某一些未知异常被触发时,
			//忘了将锁释放锁而导致程序整体被挂起
			defer func() {
				mut.Unlock()
			}()
			//共享内存加锁
			mut.Lock()
			//每完成一个协程,就执行 Done(),表示已完成
			wg.Done()
			counter++
		}()
	}

	//一直等,等到所有协程都执行完毕后,程序才往下继续执行
	wg.Wait()
	fmt.Printf("counter=%d\n", counter)
}

/*
可以看到下面的执行结果,counter 仍然等于 5000

=== RUN   TestCounterWaitGroup
counter=5000
--- PASS: TestCounterWaitGroup (0.00s)
PASS
*/

那么 WaitGroup 为什么更好呢,可以看一下最后的执行时间,如果采用 time.Sleep(),因为我们并不知道500个协程要执行多久,这个时间不好把控,我们为了得到正确的结果,人为预估了 1 秒,但是实际上只需要 0.00 秒就能执行完毕,故用 WaitGroup 即能防止错误的预估协程的执行时间,又能保证线程安全,是上上之选。

三、总结

//互斥锁
var mut sync.Mutex
//同步阻塞等待
var wg sync.WaitGroup

//每启动一个协程,我们的等待数量就要加一
wg.Add(1)
go func() {
    defer func() {
        //释放锁
        mut.Unlock()
    }()
    //共享内存加锁
    mut.Lock()
    //每完成一个协程,就执行 Done(),表示已完成
    wg.Done()
}()

//一直等,等到所有协程都执行完毕后,程序才往下继续执行
wg.Wait()

:这篇博文是我学习中的总结,如有转载请注明出处:

https://blog.csdn.net/DaiChuanrong/article/details/118269870

上一篇Go-协程机制

下一篇Go-CSP并发机制

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值