Go 中的并发

Go 中的并发 学习一下go的并发策略 线程分类 一般我们都知道线程会分为两类: 内核线程 用户线程 用户线程 用户线程在用户空间中实现,内核并没有直接对用户线程进程调度,内核的调度对象和传统进程一样,还是进程(用户进程)本身,内核并不能看到用户线程,内核并不知道用户线程的存在。 不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度和管理线程的函数来控制用户线程。 内核资源的分配仍然是按照进程(用户进程)进行分配的;各个用户线程只能在进程内进行资源竞争。 用户级线程内核的切换由用户态程序自己控制内核切换(通过系统调用来获得内核提供的服务),不需要内核干涉,少了进出内核态的消耗,但不能很好的利用多核Cpu。目前Linux pthread大体是这么做的。 每个用户线程并不具有自身的线程上下文。因此,就线程的同时执行而言,任意给定时刻每个进程只能够有一个线程在运行,而且只有一个处理器内核会被分配给该进程。 内核线程 内核线程又称为守护进程,内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的; 这些线程可以在全系统内进行资源的竞争; 内核空间内为每一个内核支持线程设置了一个线程控制块(TCB),内核根据该控制块,感知线程的存在,并进行控制。在一定程度上类似于进程,只是创建、调度的开销要比进程小。有的统计是1:10。 内核线程切换由内核控制,当线程进行切换的时候,由用户态转化为内核态。切换完毕要从内核态返回用户态,即存在用户态和内核态之间的转换,比如多核cpu,还有win线程的实现。 ps(自己的一些思考):其实可以看出来,内核线程由内核来实现创建、销毁,是内核的调度策略 用户线程的话更像是搭载在内核线程之上的,是用户自己定义调度策略,这样就可以只用一个内核线程完成多个用户线程的任务,go的调度策略就是这样的 go的线程 操作系统会在物理处理器上调度线程来运行,而Go语言的运行时会在逻辑处理器上调度goroutine来运行。 每个逻辑处理器都分别绑定到单个操作系统线程。 在1.5 版本如图所示,可以看到操作系统线程、逻辑处理器和本地运行队列之间的关系。如果创建一个goroutine 并准备运行,这个goroutine 就会被放到调度器的全局运行队列中。 之后,调度器就将这些队列中的goroutine 分配给一个逻辑处理器,并放到这个逻辑处理器对应的本地运行队列上,Go语言的运行时默认会为每个可用的物理处理器分配一个逻辑处理器。在1.5 版本之前的版本中,默认给 整个应用程序只分配一个逻辑处理器。 在图6-2 中,可以看到操作系统线程、逻辑处理器和本地运行队列之间的关系。如果创建一个goroutine 并准备运行,这个goroutine 就会被放到调度器的全局运行队列中。之后,调度器就将这些队列中的goroutine 分配给一个逻辑处理器,并放到这个逻辑处理器对应的本地运行队列中。本地运行队列中的goroutine 会一直等待直到自己被分配的逻辑处理器执行。 有时,正在运行的goroutine 需要执行一个阻塞的系统调用,如打开一个文件。当这类调用发生时,线程和goroutine 会从逻辑处理器上分离,该线程会继续阻塞,等待系统调用的返回。与此同时,这个逻辑处理器就失去了用来运行的线程。所以,调度器会创建一个新线程,并将其绑定到该逻辑处理器上。之后,调度器会从本地运行队列里选择另一个goroutine 来运行。一旦被阻塞的系统调用执行完成并返回,对应的goroutine 会放回到本地运行队列,而之前的线程会保存好(复用),以便之后可以继续使用。 基于调度器的内部算法,一个正运行的goroutine 在工作结束前,可以被停止并重新调度。调度器这样做的目的是防止某个goroutine长时间占用逻辑处理器。当goroutine 占用时间过长时,调度器会停止当前正运行的goroutine,并给其他可运行的goroutine 运行的机会。可以通过runtime.GOMAXPROCS(1)来验证这个结论 验证问题 验证1 package main import ( "fmt" "runtime" "sync" ) // main 是所有Go 程序的入口 func main() { // 分配一个逻辑处理器给调度器使用 runtime.……

阅读全文

go的反射

最近在学习go的反射,做一下笔记 基本概念 func reflectionDemo(a interface{}) { // 获取类型 t0 := reflect.TypeOf(a) fmt.Printf("type of a is:%v\n", t0) // 获取值 v := reflect.ValueOf(a) // 通过值获取类型 t1 := v.Type() fmt.Printf("type of a is:%v\n", t1) // 通过类型获得分类 kind := t1.Kind() // 通过分类来判断是什么类型的 switch kind { // 结构体 case reflect.Struct: for i := 0; i < v.NumField(); i++ { field := v.Field(i) //打印字段的名称、类型以及值 fmt.Printf("name:%s type:%v value:%v\n", t1.Field(i).Name, field.Type().Kind(), field.Interface()) } // int类型 case reflect.Int: fmt.……

阅读全文

使用Vscode 搭建 go语言环境

使用Vscode 搭建 go语言环境 去字节应该是要使用go语言开发 goland总是需要验证,激活经常过期,每次找激活码需要花费很久时间 所以改用vscode开发 步骤 初始步骤 简单的就不过多记录: * 安装go环境(环境变量、GOROOT、GOPATH) * 安装vscode * 打开vscode插件搜索 go 安装 vscode安装插件 go get被墙问题 使用代理 https://goproxy.io/ # 我只运行了前两行 go env -w GO111MODULE=on go env -w GOPROXY="https://goproxy.io,direct" # Set environment variable allow bypassing the proxy for selected modules (optional) go env -w GOPRIVATE="*.corp.example.com" ps: 安装完以后一定要关闭,不然无法启动debug,会提示go module错误 go env GO111MODULE=off ……

阅读全文

hugo + github page 搭建自己的博客

为了搭建自己的博客完善自己的知识体系 windows本地搭建 1、下载hugo 直接访问官网:https://gohugo.io/ 本来打算在ubuntu的虚拟机搭的,但是直接下载的版本有点低,和后面的主题有点不兼容,可能我用的是阿里云的apt-get源 所以直接下载windows的0.61版本 把hugo.exe拷贝到一个PATH目录下,感觉这样省事,之前设置过 2、验证安装 hugo version Hugo Static Site Generator v0.57.2-A849CB2D windows/amd64 BuildDate: 2019-08-17T17:54:13Z 3、创建站点 找一个目录,打开命令行 hugo new site blog 4、下载主题 因为现在还是一个空的站点,没有index.html,即使现在起也是一个空的 使用的一个简单主题maupassant cd themes git clone https://github.com/flysnow-org/maupassant-hugo.git maupassant 修改config.toml languageCode = "zh-cn" title = "Crazy lion's blog" theme = "maupassant" 5、创建一个blog 这个主题有一个规定文档要放在post目录下 hugo new post/my-first-post.md 6、启动服务 hugo server -D 打开浏览器查看http://localhost:1313/ github page搭建 7、编译hugo项目 hugo -D 和6的参数是一样的,之前没有-D,放上去没有内容 会生产一个public的文件夹……

阅读全文