0

0

如何在Golang中实现观察者通知机制_Golang观察者模式事件派发示例

P粉602998670

P粉602998670

发布时间:2026-01-26 16:13:02

|

981人浏览过

|

来源于php中文网

原创

Go语言可用map+sync.RWMutex+chan手动实现线程安全观察者模式:用RWMutex保护事件名到回调列表的映射,Notify异步执行并recover panic,Subscribe返回注销函数,注意函数相等性限制。

如何在golang中实现观察者通知机制_golang观察者模式事件派发示例

Go 语言没有内置的观察者模式支持,也没有类似 EventEmitter标准库类型,所以必须手动实现——但不需要第三方库,用 map + sync.RWMutex + chan 就能写出线程安全、低开销、可取消的通知机制。

如何用 mapsync.RWMutex 管理订阅者

核心是维护一个事件名到回调函数列表的映射。不能直接用 map[string][]func(interface{}),因为并发读写会 panic;也不能只靠 sync.Mutex,读多写少场景下 RWMutex 更合适。

关键点:

  • 订阅时用 RWMutex.Lock(),避免写冲突
  • 通知时用 RWMutex.RLock(),允许多个 goroutine 并发读取监听器列表
  • 回调函数签名统一为 func(interface{}),保持事件数据类型灵活
  • 不预分配 slice 容量,避免误判订阅数量(实际可能动态增删)

为什么通知逻辑要避免阻塞发布者

如果在 Notify() 中同步执行所有回调,某个慢回调(比如 HTTP 请求、日志落盘)会拖慢整个事件流,甚至导致调用方超时。更糟的是,若回调 panic,未 recover 会导致整个通知中断,后续监听器收不到事件。

立即学习go语言免费学习笔记(深入)”;

推荐做法是异步派发:

Baklib
Baklib

在线创建产品手册、知识库、帮助文档

下载
  • 每个事件通知启动独立 goroutine 执行回调,互不影响
  • recover() 捕获单个回调 panic,不传播到其他监听器
  • 不加 waitgroupchan 等待回调结束——发布者只负责“发出”,不关心“送达”

示例中 notifyAsync 方法就是按这个思路写的。

如何支持监听器动态注销和事件一次性消费

原生 map 删除键值对容易,但「注销指定回调」需要额外结构。常见错误是用闭包比较函数地址——Go 中函数变量不可比较,== 会编译失败。

可行方案有两种:

  • 返回注销函数(func()),内部记录 listener id,注销时查表删除 —— 示例采用此法,简洁且无反射开销
  • 要求用户传入唯一 string 标识符,注销时按标识删 —— 适合跨包注册场景

一次性事件(如初始化完成、资源关闭)可通过在通知后清空对应事件的监听器列表实现,无需额外字段标记。

type EventManager struct {
	mu       sync.RWMutex
	listeners map[string][]func(interface{})
}

func NewEventManager() *EventManager {
	return &EventManager{
		listeners: make(map[string][]func(interface{})),
	}
}

func (e *EventManager) Subscribe(event string, f func(interface{})) func() {
	e.mu.Lock()
	defer e.mu.Unlock()

	e.listeners[event] = append(e.listeners[event], f)

	return func() {
		e.mu.Lock()
		defer e.mu.Unlock()
		if list, ok := e.listeners[event]; ok {
			for i, fn := range list {
				if fn == f { // 注意:仅当 f 是同一函数值时才成立,适用于闭包绑定场景
					e.listeners[event] = append(list[:i], list[i+1:]...)
					break
				}
			}
		}
	}
}

func (e *EventManager) Notify(event string, data interface{}) {
	e.mu.RLock()
	list, ok := e.listeners[event]
	e.mu.RUnlock()

	if !ok || len(list) == 0 {
		return
	}

	for _, f := range list {
		go func(fn func(interface{})) {
			defer func() {
				if r := recover(); r != nil {
					// log.Printf("event %s handler panic: %v", event, r)
				}
			}()
			fn(data)
		}(f)
	}
}

注意:函数相等性判断(fn == f)在 Go 中仅对函数字面量或同一变量有效;若监听器来自不同闭包(比如带不同捕获变量),需改用标识符方式管理。这是最容易被忽略的兼容性边界。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

180

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

341

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

209

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

394

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

220

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

192

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

335

2025.06.17

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

4

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4.2万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号