并发编程

2022/6/21 1:20:18

本文主要是介绍并发编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Go 并发原语
并发问题出现的原因---> 在Go开发中如何监测到这个问题---> Go 是如何解决的

互斥锁

https://songlh.github.io/paper/go-study.pdf
问题描述:

package main
import(
	"fmt"
	"sync"
)
func main(){
	var count = 0
	// 使用 WaitGroup等待 10个goroutine 完成
	var wg sync.WaitGroup
	wg.Add(10)
	for i :=0;i< 10; i++{
		go func ()  {
			defer wg.Done()
			// 对变量count 执行10次加1
			for j:=0;j<100000; j++{
				count++
			}
		}()
	}

// 等10个goroutine 完成
wg.Wait()
fmt.Println(count)
}

image
出错原因
分析代码---> 这段代码一共启动了10个goroutine, 这10个goroutine 可能同时进行count++的操作
如何监测

Data Race Detector:https://go.dev/doc/articles/race_detector

image
groutine8 对内存地址0x00c00012c078 有度的操作 (go文件的第17行), 同时groutine7 对内存地址有写操作(go文件的第17行)
Data Race Deltector 只能在代码触发并发问题后才给予反馈
go tool compile -race -S .\testMutext.go 可以查看 go 编译后的代码,之前了解到, 产生并发问题是在 count++ 附近,重点关注一下

代码中增加了runtine.recefuncenter, runtime.recered, runtime.recewrite, runtime.recefuncexit等监测data race问题
如何解决
Go标准库中, package sync提供了锁相关的一系列同步原语,Mutex 实现了该包提供的一个 Locker的接口

type Locker interface {
    Lock()
    Unlock()
}

互斥锁Mutex 提供了两个方法 Lock和Unlock,进入临界区前调用Lock方法, 退出临界区的时候调用Unlock方法

func(m *Mutex)Lock()
func(m*Mutex)Unlock()

Mutex 嵌入到Struct中使用

type Counter struct{
    mu sync.Mutex
    Count unint64
}

使用Mutex解决并发锁问题的几种代码形式
1.

package main
import (
	"fmt"
	"sync"
)
func main(){
	// 互斥锁保护计数器
	var mu sync.Mutex
	// 计数器的值
	var count = 0
	
	// 辅助变量,用来确认所有的goroutine 都完成
	var wg sync.WaitGroup
	wg.Add(10)

	// 启动 10个gourontine
	for i:=0; i<10; i++{
		go func ()  {
			defer wg.Done()
			// 累加 10万次
			for j:=0; j< 100000;j++{
				mu.Lock()
				count++
				mu.Unlock()
			}
		}()
	}
	wg.Wait()
	fmt.Println(count)
}
package main

import (
    "fmt"
    "sync"
)

func main() {
    
    var counter Counter
    var wg sync.WaitGroup
    wg.Add(10)
    for i := 0; i < 10; i++ {
        go func() {
            defer wg.Done()
            for j := 0; j < 100000; j++ {
                counter.Lock()
                counter.Count++
                counter.Unlock()
                
            }
        }()
    }
    wg.Wait()
    fmt.Println(counter.Count)
}
type Counter struct{
    sync.Mutex
    Count uint64
}
package main
import(
	"fmt"
	"sync"
)
func main(){
	//封装好的计数器
	var counter Counter 

	var wg sync.WaitGroup
	wg.Add(10)

	//启动 10个goroutine 
	for i:=0;i<10;i++{
		go func(){
			defer wg.Done()
			// 执行10万次 累加
			for j:=0; j<100000;j++{
				counter.Incr()  //受到锁保护的方法
			}
		}()
	}
	wg.Wait()
	fmt.Println(counter.Count())
}

// 线程安全的计数器类型
type Counter struct{
	CounterType int
	Name string

	mu sync.Mutex
	count uint64
}
// 加1 的方法,内部使用互斥锁保护
func (c *Counter) Incr(){
	c.mu.Lock()
	c.count++
	c.mu.Unlock()
}

// 得到计数器的值,也需要锁保护

func (c *Counter) Count() uint64{
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.count
}


这篇关于并发编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程