Go 阻塞

阻塞 

在Go语言中,阻塞通常指的是一个goroutine(轻量级线程)在等待另一个goroutine完成操作(如I/O操作、channel通信等)时,暂时停止执行的现象。Go语言提供了多种同步和通信机制,可以用于实现阻塞的效果。

使用 Channel 实现阻塞

Channel 是Go语言中的一个核心特性,用于在goroutines之间进行通信。通过channel,你可以实现阻塞等待数据或命令。

package mainimport ("fmt""time"
)func main() {c := make(chan struct{})go func() {fmt.Println("业务处理~~~")time.Sleep(2 * time.Second)fmt.Println("业务处理完成~~~")close(c) // 关闭channel,通知工作完成}()<-c // 阻塞等待channel关闭fmt.Println("处理其他业务~~~")
}

使用 WaitGroup 实现阻塞

WaitGroup 是Go语言中用于同步一组并发操作的另一个工具。它通过计数器来跟踪完成的操作数量。

package mainimport ("fmt""strconv""sync""time"
)func main() {var wg sync.WaitGroup //控制并发组doWork := func(i int) {// wg.Done(): 表示一个事件已经完成。它等价于 wg.Add(-1),但更明确地表达了“完成一个任务”的意图,并且在使用上更安全,因为它不会导致计数变为负数(如果已经到达零,则会panic)。defer wg.Done() // 当函数返回时,通知WaitGroup一个操作已完成相当于wg.Add(-1)fmt.Println("处理业务~~~" + strconv.Itoa(i))time.Sleep(2 * time.Second)fmt.Println("业务处理完成~~~" + strconv.Itoa(i))}for i := 0; i < 5; i++ {wg.Add(1)    // 增加WaitGroup的计数器go doWork(i) // 启动一个goroutine做工作}//主goroutine调用wg.Wait(),直到所有启动的goroutines都通过调用wg.Done()通知它们已经完成工作wg.Wait() // 阻塞,直到WaitGroup的计数器为0fmt.Println("所有业务处理完成~~~")
}

使用 Mutex 和 Conditional Variables 实现阻塞

Mutex(互斥锁)和条件变量可以用来同步访问共享资源,并实现基于条件的阻塞。 

package mainimport ("fmt""sync""time"
)func main() {var mtx sync.Mutex         //创建互斥锁cond := sync.NewCond(&mtx) //使用mtx作为底层互斥锁ready := false// 启动一个 goroutine 来改变条件变量 ready 的值,并通知 cond。go func() {fmt.Println("循环跟goroutine是go内部决定先调度的--------------------goroutine--------------------")time.Sleep(3 * time.Second)mtx.Lock() //使用互斥锁ready = truecond.Signal() // 唤醒至少一个等待的 goroutinemtx.Unlock()  //解锁}()mtx.Lock() // 锁定互斥锁,准备进入条件等待for !ready {fmt.Println("循环跟goroutine是go内部决定先调度的--------------------阻塞--------------------")cond.Wait() // 阻塞,直到 cond.Signal() 被调用//mtx.Unlock()}mtx.Unlock() // 解锁互斥锁,继续执行(此处mtx.Unlock()在for循环里面阻塞等待完成后也可以,也可以没有,因为主线程会结束,但如果后续还需要获取互斥锁则必须要释放否则报错)fmt.Println("准备继续~~~")
}

这里是一些关键的修改和注意事项:

  1. sync.Cond 的使用需要一个 sync.Mutex 作为其底层的互斥锁。在使用 cond.Wait() 之前,必须先锁定这个互斥锁。

  2. cond.Wait() 调用中,当前的互斥锁会被自动释放,goroutine 会阻塞直到它被 cond.Signal()cond.Broadcast() 唤醒。

  3. 一旦 cond.Wait() 返回,goroutine 会重新获取互斥锁,然后继续执行循环或代码块。

  4. cond.Signal() 调用之后,您需要在某个地方调用 mtx.Unlock() 来释放互斥锁,否则主 goroutine 会在 cond.Wait() 之后无法获取到锁。

  5. 您的代码中,cond.Wait() 之后的 mtx.Unlock() 应该在 for 循环之外,以避免在循环的每次迭代中重复加锁和解锁。 

在Go语言中,sync.Mutex(互斥锁)用于保护共享资源不被多个goroutine同时修改,以避免竞态条件。sync.Cond(条件变量)与互斥锁结合使用,可以在多个goroutine之间同步共享条件。以下是关于何时使用 mtx.Lock()mtx.Unlock() 的指导:

mtx.Lock()

  • 在访问或修改由互斥锁保护的共享资源之前使用。
  • 在调用 cond.Wait() 之前使用,以确保在等待条件变量时,共享资源不会被其他goroutine并发访问。
  • 在调用 cond.Signal() 或 cond.Broadcast() 之前使用,因为这些操作需要在互斥锁保护的临界区内执行。

mtx.Unlock()

  • 在完成对共享资源的访问或修改后使用。
  • 在 cond.Wait() 返回后使用,因为我们已经完成了等待期间需要的共享资源访问,并且需要重新获取互斥锁以继续执行。
  • 在不再需要互斥锁保护当前goroutine的执行路径时使用,以允许其他等待互斥锁的goroutine继续执行。

注意事项

  • 互斥锁必须在获取后及时释放,否则会导致死锁。
  • 通常,获取互斥锁和释放互斥锁成对出现,以避免忘记释放锁。

永久阻塞

Go 的运行时的当前设计,假定程序员自己负责检测何时终止一个 goroutine 以及何时终止该程序。可以通过调用 os.Exit 或从 main() 函数的返回来以正常方式终止程序。而有时候我们需要的是使程序阻塞在这一行。

使用 sync.WaitGroup 

一直等待直到 WaitGroup 等于 0 

package mainimport "sync"func main() {var wg sync.WaitGroupwg.Add(1)wg.Wait()
}

空 select

 select{}是一个没有任何 case 的 select,它会一直阻塞

package mainfunc main() {select{}
}

 死循环

虽然能阻塞,但会 100%占用一个 cpu。不建议使用

package mainfunc main() {for {}
}

 用 sync.Mutex

一个已经锁了的锁,再锁一次会一直阻塞,这个不建议使用

package mainimport "sync"func main() {var m sync.Mutexm.Lock()
}

 os.Signal

系统信号量,在 go 里面也是个 channel,在收到特定的消息之前一直阻塞  

package mainimport ("os""os/signal""syscall"
)func main() {sig := make(chan os.Signal, 2)//syscall.SIGTERM 是默认的终止进程信号,通常由服务管理器(如systemd、supervisor等)发送来请求程序正常终止。//syscall.SIGINT 是中断信号,一般由用户按下Ctrl+C键触发,用于请求程序中断执行signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)<-sig
}
从终端发送信号
  • Ctrl+C: 在大多数Unix-like系统(包括Linux和macOS)以及Windows的命令行中,按 Ctrl+C 键会向当前前台进程发送一个 SIGINT(中断)信号。这通常是停止Go程序的快捷方式。

  • Kill命令: 如果你的程序在后台运行,并且你知道其进程ID(PID),可以通过终端发送一个信号。例如,发送一个 SIGTERM 信号,可以使用:kill PID或者指定型号类型kill -SIGTERM PID

 从Go代码内部发送信号
package mainimport ("os""os/signal""syscall""time"
)func main() {sig := make(chan os.Signal, 2)//syscall.SIGTERM 是默认的终止进程信号,通常由服务管理器(如systemd、supervisor等)发送来请求程序正常终止。//syscall.SIGINT 是中断信号,一般由用户按下Ctrl+C键触发,用于请求程序中断执行signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)go func() {time.Sleep(10 * time.Second)sig <- syscall.SIGTERM}()go func() {time.Sleep(5 * time.Second)sig <- syscall.SIGINT}()<-sig
}
使用外部工具或服务管理器

如果你的Go程序作为服务运行,可能由如systemd、supervisord等服务管理器控制,这些管理器通常提供了发送信号给托管服务的机制。具体操作需参考相应服务管理器的文档。

空 channel 或者 nil channel 

channel 会一直阻塞直到收到消息,nil channel 永远阻塞。 

package mainfunc main() {c := make(chan struct{})<-c
}
package mainfunc main() {var c chan struct{} //nil channel<-c
}
 总结

 注意上面写的的代码大部分不能直接运行,都会 panic,提示“all goroutines are asleep - deadlock!”,因为 go 的 runtime 会检查你所有的 goroutine 都卡住了, 没有一个要执行。

你可以在阻塞代码前面加上一个或多个你自己业务逻辑的 goroutine,这样就不会 deadlock 了。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/11640.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据赋能(86)——数据要素:管理核心框架

数据管理的核心框架是一个综合性的体系&#xff0c;旨在确保数据的有效利用、安全性以及合规性。这个框架主要包含了以下几个关键组成部分&#xff1a; 数据治理策略与目标&#xff1a;明确数据管理的整体战略和目标&#xff0c;包括数据价值的释放、数据资产地位的确定、多元…

OpenHarmony 实战开发——移植通信子系统

通信子系统目前涉及Wi-Fi和蓝牙适配&#xff0c;厂商应当根据芯片自身情况进行适配。 移植指导 Wi-Fi编译文件内容如下&#xff1a; 路径&#xff1a;“foundation/communication/wifi_lite/BUILD.gn” group("wifi") {deps [ "$ohos_board_adapter_dir/ha…

C++基础与深度解析 | 数组 | vector | string

文章目录 一、数组1.一维数组2.多维数组 二、vector三、string 一、数组 1.一维数组 在C中&#xff0c;数组用于存储具有相同类型和特定大小的元素集合。数组在内存中是连续存储的&#xff0c;并且支持通过索引快速访问元素。 数组的声明&#xff1a; 数组的声明指定了元素的…

前端人员如何理解进程和线程

进程和线程的概念&#xff1a; 进程和线程本质都是cpu工作过程的时间片。 进程可以理解为cpu在运行指令即加载保存上下文所要用的时间。也可以理解为一个应用程序运行的实例。 线程是进程中更小的单位&#xff0c;描述一段指令所需要的时间。 进程是资源分配的最小单位&#xf…

【数据结构】数组循环队列的实现

队列&#xff08;Queue&#xff09;是一种特殊的线性数据结构&#xff0c;它遵循FIFO&#xff08;First In First Out&#xff0c;先入先出&#xff09;的原则。队列只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#…

MySQL中导出CSV格式数据 | Java处理CSV数据

1. 导出不带表头的CSV数据 SELECT dataid, recordfilename INTO OUTFILE /tmp/uk_callcenter_event3.csv FIELDS TERMINATED BY , LINES TERMINATED BY \n FROM table_name WHERE createtime > 2024-03-27 22:00:00 AND createtime < 2024-04-29 23:59:59 AND timehou…

使用selenium控制已经打开的浏览器,应该如何实现。

要使用Selenium控制一个已经打开的浏览器实例&#xff0c;你可以通过以下步骤实现&#xff0c;这里以Google Chrome浏览器为例&#xff1a; 步骤 1: 启动Chrome浏览器并启用远程调试 首先&#xff0c;你需要以远程调试模式启动Chrome浏览器。这可以通过在命令行中使用特定参数来…

python下载及安装

1、python下载地址&#xff1a; Python Releases for Windows | Python.orgThe official home of the Python Programming Languagehttps://www.python.org/downloads/windows/ 2、python安装 &#xff08;1&#xff09; 直接点击下载后的可执行文件.exe &#xff08;2&…

Spring Boot项目怎么集成Gitee登录

一、背景 现在的越来越多的项目&#xff0c;需要集成第三方系统进行登录。今天我们以Spring Boot项目集成Gitee为例&#xff0c;演示一下怎么使用Oauth2协议&#xff0c;集成第三方系统登录。 不了解oauth2的&#xff0c;可以看我之前的文章。Ouath2是怎么实现在第三方应用认…

MySQL创建储存过程函数

DDL CREATE TABLE student (id int(11) NOT NULL AUTO_INCREMENT COMMENT 学号,createDate datetime DEFAULT NULL COMMENT 创建时间,modifyDate datetime DEFAULT NULL COMMENT 修改时间,userName varchar(30) NOT NULL COMMENT 学生名称,pwd varchar(36) DEFAULT NULL COMME…

代码随想录算法训练营第五十二天

今日效率低下&#xff0c;努力把题做完。做快一点&#xff01;&#xff01;&#xff01; 300.最长递增子序列 class Solution { public:int lengthOfLIS(vector<int>& nums) {if (nums.size() 1) return 1;vector<int>dp(nums.size(),1);int result 0;for(i…

计算机毕业设计Python+Spark知识图谱课程推荐系统 课程预测系统 课程大数据 课程数据分析 课程大屏 mooc慕课推荐系统 大数据毕业设计

1 绪 论 1.1 课题研究背景 在线教育学习平台是学生用来进行校内或校外拓展课程学习的平台&#xff0c;平台需要具备在线视频观看&#xff0c;作业提交&#xff0c;形成性考核等功能。在学生学习的过程中&#xff0c;学校的管理者或负责教师需要了解学生的学习情况和学习状态&…

Spring STOMP-发送消息

如果你想要从应用程序的任何地方向连接的客户端发送消息&#xff0c;要怎么做&#xff1f;任何应用程序组件都可以向brokerChannel发送消息。要这样做&#xff0c;最简单方法是注入一个SimpMessagingTemplate并使用它来发送消息。通常&#xff0c;你会按类型注入它&#xff0c;…

WWW服务器搭建(2)——Apache服务器配置与管理

一、Apache简介 1.1 关于Apache Apache HTTP Server&#xff08;简称Apache&#xff09;是Apache软件基金会的一个开放源码的Web服务器&#xff0c;可以在大多数计算机操作系统中运行&#xff0c;由于其跨平台和安全性被广泛使用&#xff0c;是最流行的Web服务器端软件之一。…

01-02-5

1、单链表中按位置查找 a.原理 通过传递的位置&#xff0c;返回该位置对应的地址&#xff0c;放到主函数定义的指针变量中。 我们认为位置从&#xff1a;有数据的节点开始计数 即如下结构&#xff1a; 查找位置&#xff0c;就是返回该位置对应的空间地址。 b.代码说明 Ⅰ…

H5嵌入原生----兼容安卓与ios

主要分UI展示&#xff0c;键盘&#xff0c;输入框等等。解决bug最苦恼的问题不是没有解决方案&#xff0c;而是你没有找到真正的原因。再就是现象难以重现&#xff0c;每次都要发布代码&#xff0c;然后到手机app中去测试&#xff0c;模拟。这些地方会耗费大量的精力。 一、UI…

【软设】常见易错题汇总

目录 计算机系统基础 程序语言基础 数据结构 算法设计与分析 计算机网络与信息安全 软件工程基础 开发方法&#xff08;结构化与面向对象&#xff09; 数据库 操作系统 知识产权相关的法律法规 &#x1f92f;&#x1f92f;&#x1f92f;&#x1f92f;&#x1f92f;&#x1f9…

《系统架构设计师教程(第2版)》第10章-软件架构的演化和维护-07-软件架构维护

文章目录 1. 软件架构知识管理1.1 概念1.2 架构知识的获取1.3 作用1.4 架构知识管理的现状 2 软件架构修改管理3 软件架构版本管理4. 示例4.1 背景4.2 数据获取4.3 数据计算4.4 结果分析4.4.1 圈复杂度 (CCN)4.4.2 扇入扇出度 (FFC)4.4.3 模块间耦合度 (CBO)4.4.4 模块的响应 (…

mysql group by 细节介绍

mysql中group by的用法是配合聚合函数&#xff0c;利用分组信息进行统计&#xff0c;语句如“select name,sum(id) from test group by name,number”。 先来看下表1&#xff0c;表名为test&#xff1a; 执行如下SQL语句&#xff1a; SELECT name FROM test GROUP BY name 你…

OFDM802.11a的FPGA实现(十四)data域的设计优化,挤掉axi协议传输中的气泡

原文链接&#xff08;相关文章合集&#xff09;&#xff1a;OFDM 802.11a的xilinx FPGA实现 目录 1.前言 2.data域的时序要求 3.Debug 1.前言 前面12篇文章详细讲述了&#xff0c;OFDM 802.11a发射部分data域的FPGA实现和验证&#xff0c;今天对data域的设计做一个总结。在…