当前位置: 首页 > article >正文

每日一题:golang并发 mutex、context

题目

对变量执行2000次+1操作
5个协程并发执行,且支持通过 context 取消操作

package test

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


type num struct {
	num int
	sync.Mutex
	sync.WaitGroup
}

func AddWithLock(ctx context.Context, n *num) {
	for i := 0; i < 2000; i++ {
		select {
		case <-ctx.Done():
			fmt.Println("ctx time out")
			n.Done()
			return
		default:
			n.Lock()
			n.num++
			n.Unlock()
		}
	}
	n.Done() // 实际上调用 Done() 后,当前上下文会通知它的所有子上下文取消,但它不会影响父 context 或其他兄弟上下文。
}
func TestLock(t *testing.T) {
	ctx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
	n := &num{
		num:       0,
		Mutex:     sync.Mutex{},
		WaitGroup: sync.WaitGroup{},
	}
	n.WaitGroup.Add(5)

	defer cancelFunc()
	for i := 0; i < 5; i++ {
		go AddWithLock(ctx, n)
	}
	n.Wait()

	fmt.Println(n.num)
}

总结

并发中:注意结构体使用指针而不是副本

如果结构体中包含sync.Mutex 和 sync.WaitGroup这些需要注意:

  1. num 结构体的复制问题:
    num 结构体包含了 sync.Mutex 和 sync.WaitGroup,这两个类型是不安全被复制的,因为 Mutex 和 WaitGroup 是带有状态的对象。将 num 类型传递给 goroutine 时,会复制 num 实例

你应该使用指针传递 num,而不是传递结构体的副本。

go AddWithLock(ctx, &n)

以及 AddWithLock 函数的参数也应该改为 *num:

func AddWithLock(ctx context.Context, n *num) {
  1. WaitGroup 的并发修改问题:
    在并发场景中,WaitGroup 的 Add() 和 Done() 操作需要在同一个 goroutine 内进行管理。如果你将 WaitGroup 对象作为结构体成员传递给 goroutine,Done() 操作和 Add() 可能会出现并发问题。
    修复建议:
    使用指针来传递 WaitGroup,保证 WaitGroup 在并发过程中是安全的。这里修改的关键是将 Add(5) 和 Done() 保证正确调用。

  2. select 语句中的超时处理:
    在你的 AddWithLock 函数中,当 ctx.Done() 被触发时,你调用了 n.Done() 来标记 goroutine 完成。这里是正确的,但由于 n 是结构体的副本,这个操作并不会影响主函数中的 WaitGroup,因此主线程会一直等待直到超时。

mutex底层实现

TODO

channel底层实现及使用注意

TODO

context 传递取消

  • 对子上下文:当父 context 被取消时,所有由它创建的子 context 都会受到影响,会接收到取消信号,Done() 会关闭。
  • 对子上下文的影响:调用 Done() 后,当前上下文会通知它的所有子上下文取消,但它不会影响父 context 或其他兄弟上下文。
  • 上游上下文:父上下文的 Done() 通道只有在父上下文本身调用 Cancel 时才会关闭,不会因为子上下文的 Done() 被关闭而受到影响。

取消原理:

  1. 一个context被取消后,会将该ctx从其父ctx的children中移除,并且传播取消信号
  2. 取消时会关闭Done()对应的只读channel,因此在select那边监听这个channel时,一旦取消,会立即返回一个零值,就监听到取消了

http://www.kler.cn/a/422080.html

相关文章:

  • 【论文阅读】Adversarial gradient-based meta learning with metric-based test
  • 什么是 Socket?
  • 005 MATLAB符号微积分
  • 数仓项目常见问题解答
  • Narya.ai正在寻找iOS工程师!#Mixlab内推
  • linux下环境变量的使用
  • 基于Java Springboot时间管理微信小程序
  • NLP 相关知识的构成
  • MR30分布式 IO 模块助力印刷设备,开启收益新篇
  • python脚本:Word文档批量转PDF格式
  • el-input输入校验,只允许输入数字或者带一个小数点的数字。
  • Linux环境部署RocketMQ单节点以及双主双从集群环境
  • 算法笔记:力扣146.LRU缓存
  • ubuntu无线网络共享到有线
  • 在 Windows Server 2022 Datacenter 上配置 MySQL 8.0 的主从复制
  • vscode切换anaconda虚拟环境解释器不成功
  • 无人机的计算机仿真模拟控制
  • 【测试工具JMeter篇】JMeter性能测试入门级教程(六):JMeter中实现参数化的几种方式
  • 利用Python爬虫获取淘宝关键词接口的深入解析
  • LeetCode78:子集