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.tpcf.cn/web/11640.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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域的设计做一个总结。在…