你是否想知道如何应对高并发?Go语言为你提供了答案!
并发编程是当前软件领域中不可忽视的一个关键概念。随着CPU等硬件的不断发展,我们都渴望让我们的程序运行速度更快、更快。而Go语言在语言层面天生支持并发,充分利用现代CPU的多核优势,这也是Go语言能够广泛流行的一个重要原因。
在Java中,要支持高并发有几种方案可供选择。首先,我们可以通过开启多部署节点集群来增加高并发处理能力,通过增加机器硬件来实现。其次,我们可以在单节点上开启多线程来处理请求。然而,即使在单节点内创建线程也是非常耗费资源的。因此,通常情况下我们会使用线程池来管理线程的创建和销毁。然而,有一个公式你可能会很熟悉,即核心线程数等于CPU核数的一半加一。这意味着我们并不是线程创建得越多,对于我们的Java程序就越好。
在我们明确了问题的痛点之后,我们可以进一步探究一下Go语言是如何解决这些问题,并且将高并发作为Go语言的一项特色功能。
goroutine
我们在Java中开启线程的方式是直接创建一个Thread对象。然而,在Go语言中,如果我们想要实现异步处理,我们可以使用"go"关键字来开启一个goroutine协程。协程的最大优势在于其轻量级,可以轻松创建上百万个协程而不会导致系统资源的耗尽,而线程和进程通常最多也不能超过1万个。举个例子:
go f() // 创建一个新的 goroutine 运行函数f
在Go语言中,我们可以非常简单地使用关键字"go"来开启一个协程,从而实现异步处理函数f。只需在函数f的调用前面加上"go"关键字,就能使得该函数在一个独立的协程中异步执行。
不仅可以使用"go"关键字来开启一个协程异步执行具名函数,还可以使用"go"关键字来开启一个协程异步执行匿名函数。
go func(){
// ...
}()
今天我们的重点不在这里,而是要讨论为什么Go语言适合处理高并发的情况。我们都知道,操作系统的CPU最小调度单位是线程,然而Go语言却使用了协程的概念。那么问题来了,Go语言是如何将这些协程交给CPU来处理的呢?如果无法将它们交给CPU处理,那么就算再创建多少协程也无法运行代码。在这里,我们就需要了解一下Go语言的调度器,也就是GPM调度模型。
GPM调度模型
可以借鉴一下以下图例,总的来说,我们可以像线程池一样,无论创建了多少协程,都需要将它们放入队列中。然后,剩下的任务就交给调度器来处理。
其中:
- G:使用关键字"go"加上一个函数调用可以创建一个goroutine(简称G)。每次调用"go f()"都会创建一个新的G,其中包含要执行的函数f以及相关的上下文信息。
- 全局队列(Global Queue)是用来存放等待运行的 G(Goroutine)的地方。
- P 是指 goroutine 执行所需的物理资源,每个 P 最多可以承载 GOMAXPROCS 个 goroutine 的执行。
- P 的本地队列是类似于全局队列的,它存放了等待运行的G,并且数量限制在256个以内。每当新建一个G时,优先将其加入到P的本地队列中,如果本地队列已满,则会批量移动部分G到全局队列中。
- 为了使线程能够执行任务,需要通过获取调度器(P)来获取任务(G)。线程首先尝试从调度器的本地队列获取任务,如果本地队列为空,则线程会尝试从全局队列或其他调度器的本地队列获取任务。一旦线程获取到任务,就会执行任务,并在任务执行完毕后再次从调度器获取下一个任务,持续重复这个过程。
Goroutine 调度器和操作系统调度器通过 M 结合起来,形成了调度的基本单位。在这个结合中,每个 M 代表一个内核线程,而操作系统调度器则负责将这些内核线程分配到 CPU 的核心上进行执行。
channel
单纯地将函数并发执行是没有意义的,因为函数与函数之间需要进行数据交换,才能真正体现并发执行函数的意义。
虽然可以利用共享内存进行数据交换,但是在不同的 goroutine 中使用共享内存容易导致竞态问题的出现。为了确保数据交换的正确性,许多并发模型都需要通过使用互斥量对内存进行加锁来解决这个问题。然而,这种做法往往会带来性能问题,因为加锁操作会引入额外的开销。
Go语言采用的并发模型是CSP(Communicating Sequential Processes),这个模型强调了通过通信共享内存的方式来实现并发,而不是通过共享内存来实现通信。这种设计理念使得Go语言在处理并发任务时更加高效和安全。
如果说 goroutine 是Go程序中实现并发执行的主体,那么channel就是连接这些goroutine之间的纽带。channel是一种能够使得一个goroutine向另一个goroutine发送特定值的通信机制。
Mutex(互斥锁)在实现上也是使用了重量级锁。与Java的互斥锁相比,Go语言的Mutex有以下几点区别:
内存开销:Go语言的Mutex相对较轻量,使用较少的内存。这是因为Go语言的Mutex只包含一个字段,用于表示锁的状态,而Java的互斥锁通常包含更多的字段和数据结构。
锁的语法:在Go语言中,可以使用mutex.Lock()和mutex.Unlock()方法来手动控制锁的获取和释放,这样可以更灵活地控制锁的粒度。而在Java中,使用synchronized关键字来实现互斥锁,锁的粒度相对固定,只能对整个方法或代码块进行加锁。
锁的性能:由于Go语言的Mutex较为轻量,并且采用了更高效的实现方式,比如以下几个方面:
- 自旋锁:在低并发的情况下,Go语言的Mutex会采用自旋锁的方式。自旋锁是一种忙等待的锁,当一个Goroutine尝试获取锁时,如果锁已经被其他Goroutine持有,则该Goroutine会一直循环检查锁的状态,直到成功获取锁。这种方式避免了线程切换的开销,提高了性能。
- 优化的调度策略:Go语言的调度器在处理Goroutine的调度时会进行优化,尽量将锁的持有者与等待者调度到同一个处理器(P)上执行,减少线程之间的上下文切换和锁竞争的开销。
- 等待队列:当一个Goroutine无法获取到Mutex锁时,它会进入等待队列,等待锁的释放。Go语言的Mutex的等待队列是基于链表实现的,相比Java的互斥锁使用的等待队列,具有更低的内存开销和更高的效率。
总结
并发编程是当前软件领域中一个重要的概念。Go语言通过goroutine和channel的特性,天生支持高并发处理,充分利用现代CPU的多核优势。与Java相比,Go语言的协程更加轻量级,可以轻松创建上百万个协程。Go语言的调度器采用GPM调度模型,通过将协程放入队列中,由调度器分配给CPU处理。此外,Go语言采用CSP模型,通过channel实现协程之间的通信,避免了共享内存带来的竞态问题。相比之下,Go语言的Mutex锁更轻量、灵活,并且具有更高的性能。总的来说,Go语言适合处理高并发的情况,成为了当前软件开发领域的热门语言之一。
你是否想知道如何应对高并发?Go语言为你提供了答案!的更多相关文章
- Memcached笔记——(四)应对高并发攻击【转】
http://snowolf.iteye.com/blog/1677495 近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源.他们的最 ...
- SpringCloud应对高并发的思路
一.Eureka的高可用性 Eureka下面的服务实例默认每隔30秒会发送一个HTTP心跳给Eureka,来告诉Eureka服务还活着,每个服务实例每隔30秒也会通过HTTP请求向Eureka获取服务 ...
- 《即时消息技术剖析与实战》学习笔记10——IM系统如何应对高并发
一.IM 系统的高并发场景 IM 系统中,高并发多见于直播互动场景.比如直播间,在直播过程中,观众会给主播打赏.送礼.发送弹幕等,尤其是明星直播间,几十万.上百万人的规模一点也不稀奇.近期随着武汉新型 ...
- Memcached笔记——(四)应对高并发攻击
近半个月过得很痛苦,主要是产品上线后,引来无数机器用户恶意攻击,不停的刷新产品各个服务入口,制造垃圾数据,消耗资源.他们的最好成绩,1秒钟可以并发6次,赶在Database入库前,Cache进行Mis ...
- Linux下配置tomcat+apr+native应对高并发
摘要:在慢速网络上Tomcat线程数开到300以上的水平,不配APR,基本上300个线程狠快就会用满,以后的请求就只好等待.但是配上APR之后,Tomcat将以JNI的形式调用Apache HTTP服 ...
- 【Redis】1、Jedis对管道、事务以及Watch的操作来应对高并发
对于一个互联网平台来说,高并发是经常会遇到的场景.最有代表性的比如秒杀和抢购.高并发会出现三个特点: 1.高并发读取 2.高并发写入(一致性) 3.出现超卖问题 前端如何应对? 1.缓存静态数据,例如 ...
- (转)Linux下配置tomcat+apr+native应对高并发
摘要:在慢速网络上Tomcat线程数开到300以上的水平,不配APR,基本上300个线程狠快就会用满,以后的请求就只好等待.但是配上APR之后,Tomcat将以JNI的形式调用Apache HTTP服 ...
- 想玩转JAVA高并发,这些概念你必须懂!
我们在找工作时,经常在招聘信息上看到有这么一条:有构建大型互联网服务及高并发等经验,你第一时间想到的是媒体常说的双十一吗?带着问题,我们一起思考技术…. 高并发高并发 它是互联网分布式系统架构设计中必 ...
- 想玩转JAVA高并发,这些概念你必须懂
高并发高并发 它是互联网分布式系统架构设计中必须考虑的因素之一,通常是指,保证系统能够同时并行化处理海量请求 同步和异步 同步:发送一个请求,等待返回,然后再发送下一个请求.提交请求 -> 等待 ...
- 基于tomcat为了应对高并发模型实现webserver
在博客上,一个简单的AIOweb来样加工.查看AIO异步处理,依靠操作系统完成IO操作Proactor处理模型确实很强大,它可以实现高并发.高响应server一个很好的选择,但在tomcat中间con ...
随机推荐
- KRPANO资源分析工具下载720YUN全景图
提示:目前分析工具中的全景图下载功能将被极速全景图下载大师替代,相比分析工具,极速全景图下载大师支持更多的网站(包括各类KRPano全景网站,和百度街景) 详细可以查看如下的链接: 极速全景图下载大师 ...
- SSM-Mybatis笔记
目录 Mybatis-9.28 1.简介 1.1.什么是Mybatis 1.2.持久化 1.3.持久层 1.4 为什么需要Mybatis? 2.第一个Mybatis程序 2.1.搭建环境 2.2.创建 ...
- spark修改控制台输出日志级别
spark修改控制台输出日志级别 修改conf/log4j.properties cd $SPARK_HOME/conf cp log4j.properties.template ./log4j.pr ...
- 算法打卡|Day5 哈希表part01
哈希表 part01 今日任务 ● 哈希表理论基础 ● 242.有效的字母异位词 ● 349. 两个数组的交集 ● 202. 快乐数 ● 1. 两数之和 目录 哈希表 part01 链表理论基础 Pr ...
- 开源.NetCore通用工具库Xmtool使用连载 - 扩展动态对象篇
[Github源码] <上一篇> 介绍了Xmtool工具库中的图形验证码类库,今天我们继续为大家介绍其中的扩展动态对象类库. 扩展动态对象是整个工具库中最重要的一个设计.在软件开发过程中, ...
- HCTF 2023 wp
HCTF 2023 wp 一.Misc 1.玩原神玩的 分析:附件为一张图片 观察最后一行,明显有flag的格式 搜索得知是 对照得flag为:hctf{yuanlainiyewanyuanshenh ...
- P9580 「Cfz Round 1」Wqs Game 题解
题目链接 挺好的博弈论题,这是一个跟官方题解不太一样的做法. 遇到这种组合游戏可以先考虑逆推胜负,把握一下规律,我们先从一个区间的胜负判断开始入手. 考察区间中最后一个数字的从属关系,如果它属于弈,因 ...
- alibaba fastjson的JsonObject有序的实现和源码分析
介绍 FastJson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean.在使用的过程中, ...
- Python 利用pandas和matplotlib绘制柱状折线图
创建数据可视化图表:柱状图与折线图结合 在数据分析和展示中,经常需要将数据可视化呈现,以便更直观地理解数据背后的趋势和关联关系.本篇文章将介绍如何使用 Python 中的 Pandas 和 Matpl ...
- 快速入门:构建您的第一个 .NET Aspire 应用程序
前言 云原生应用程序通常需要连接到各种服务,例如数据库.存储和缓存解决方案.消息传递提供商或其他 Web 服务..NET Aspire 旨在简化这些类型服务之间的连接和配置.在本快速入门中,您将了解如 ...