Below you will find pages that utilize the taxonomy term “Golang”
January 16, 2023
Golang 中网络请求使用指定网卡
"\u003cp\u003e当用户发起一个网络请求时,流量会通过默认的网卡接口流出与流入,但有时需要将流量通过指定的网卡进行流出流入,这时我们可能需要进行一些额外的开发工作,对其实现主要用到了 \u003ccode\u003eDialer.Control\u003c/code\u003e 配置项。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etype Dialer struct {\n\n // If Control is not nil, it is called after creating the network\n // connection but before actually dialing.\n //\n // Network and address parameters passed to Control method are not\n // necessarily the ones passed to Dial. For example, passing \u0026#34;tcp\u0026#34; to Dial\n // will cause the Control function to be called with \u0026#34;tcp4\u0026#34; or \u0026#34;tcp6\u0026#34;. …\u003c/code\u003e\u003c/pre\u003e"
February 25, 2022
一文看懂Golang 定时器源码
"\u003cp\u003e计时器分 Timer 和 Ticker 两种,它们底层基本是一样的,两差的区别请参考 , 这里我们的介绍对象是 Timer 。\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2022/03/fcabf1624c9030edb67ec1e8bf7ad4d9.png\" alt=\"\"\u003egolang timer\u003c/p\u003e\n\u003ch1 id=\"计时器结构体.wp-block-heading\"\u003e计时器结构体\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e // NewTimer creates a new Timer that will send\n // the current time on its channel after at least duration d.\n func NewTimer(d Duration) *Timer {\n c := make(chan Time, 1)\n t := \u0026amp;Timer{\n C: c,\n r: runtimeTimer{\n when: when(d),\n f: sendTime,\n arg: c,\n },\n }\n startTimer(\u0026amp;t.r)\n return t\n }\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e通过调用 \u003ccode\u003eNewTimer()\u003c/code\u003e 函数创建一个 \u003ccode\u003eTimer\u003c/code\u003e,首先创建一个长度 …\u003c/p\u003e"
November 25, 2021
Golang常见编译参数
"\u003cp\u003e在执行 go build 命令的时候,经常需要添加一些参数,或许是为了调试,也或许是为了生成最终部署二进制文件。\u003c/p\u003e\n\u003cp\u003e在编译特定包时需要传递参数,格式应遵守“包名=参数列表”,如\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ego build -gcflags -gcflags=\u0026#39;log=-N -l\u0026#39; main.go\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"-gcflags\"\u003e-gcflags\u003c/h2\u003e\n\u003cp\u003ego build 可以用 \u003cem\u003e-gcflags\u003c/em\u003e 给_go_编译器传入参数,也就是传给 go tool compile 的参数,因此可以用 go tool compile –help 查看所有可用的参数。\u003c/p\u003e\n\u003cp\u003e其中 -m 可以检查代码的编译优化情况,包括逃逸情况和函数是否内联。\u003c/p\u003e\n\u003ch2 id=\"-ldflags\"\u003e-ldflags\u003c/h2\u003e\n\u003cp\u003ego build用 -ldflags 给go链接器传入参数,实际是给go tool link的参数,可以用go tool link –help查看可用的参数。\u003c/p\u003e\n\u003cp\u003e常用-X来指定版本号等编译时才决定的参数值。例如代码中定义var buildVer string,然后在编译时用go build -ldflags “-X main.buildVer=1.0” … 来赋值。注意-X只能给string类型变量赋值。\u003c/p\u003e"
November 25, 2021
Golang中的 CGO_ENABLED 环境变量
"\u003cp\u003eGolang中的编译参数\u003c/p\u003e\n\u003cp\u003e开发中经常使用 \u003ccode\u003ego build\u003c/code\u003e 命令来编译我们的程序源码,然后将生成二进制文件直接部署,极其方便。\u003c/p\u003e\n\u003cp\u003e对于 \u003ccode\u003ego build\u003c/code\u003e 有一些参数,对于针对程序源码进行一些编译优化,下面我们对经常使用的一些参数来介绍一下。\u003c/p\u003e\n\u003ch1 id=\"环境变量\"\u003e环境变量\u003c/h1\u003e\n\u003cp\u003e环境变量需要在go命令前面设置,如果多个变量的话,中间需要用“空格”分隔。下面我们介绍一个非常常见到的一些环境变量\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ CGO_ENABLED=1 GOARCH=amd64 GOOS=linux go build -o myserver main.go\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e除了这里给出的这几个变量外,还有一些其它变量,如 GODEBUG、GOFLAGS、GOPROXY 等,所有支持环境变量都可以在 里找到,有兴趣的话可以看看他们的作用。\u003c/p\u003e\n\u003cp\u003e这里重点介绍一下 \u003ccode\u003eCGO_ENABLED\u003c/code\u003e 环境变量对我们程序的影响。 \u003ccode\u003eCGO_ENABLED\u003c/code\u003e是用来控制golang 编译期间是否支持调用 cgo 命令的开关,其值为1或0,默认情况下值为1,可以用 \u003ccode\u003ego env\u003c/code\u003e 查看默认值。\u003c/p\u003e\n\u003cp\u003e如果你的程序里调用了cgo 命令,此参数必须设置为1,否则将编译时出错。这里直接用文档 中的一个例 …\u003c/p\u003e"
May 23, 2021
Golang中的runtime.LockOSThread 和 runtime.UnlockOSThread
"\u003cp\u003e在runtime中有 \u003ccode\u003e[runtime.LockOSThread](https://github.com/golang/go/blob/go1.16.3/src/runtime/proc.go#L4248-L4278)\u003c/code\u003e 和 \u003ccode\u003e[runtime.UnlockOSThread](https://github.com/golang/go/blob/go1.16.3/src/runtime/proc.go#L4302-L4323)\u003c/code\u003e 两个函数,这两个函数有什么作用呢?我们看一下标准库中对它们的解释。\u003c/p\u003e\n\u003ch2 id=\"runtimelockosthread\"\u003eruntime.LockOSThread\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// LockOSThread wires the calling goroutine to its current operating system thread.\n// The calling goroutine will always execute in that thread,\n// and no other goroutine will execute in it,\n// until the calling goroutine has made …\u003c/code\u003e\u003c/pre\u003e"
May 10, 2021
Runtime: goroutine的暂停和恢复源码剖析
"\u003cp\u003e上一节《 \u003ca href=\"https://blog.haohtml.com/archives/27003\"\u003eGC 对根对象扫描实现的源码分析\u003c/a\u003e》中,我们提到过在GC的时候,在对一些goroutine 栈进行扫描时,会在其扫描前触发 G 的暂停(\u003ccode\u003e[suspendG](https://github.com/golang/go/blob/go1.16.2/src/runtime/preempt.go#L76-L254)\u003c/code\u003e)和恢复(\u003ccode\u003e[resumeG](https://github.com/golang/go/blob/go1.16.2/src/runtime/preempt.go#L256-L280)\u003c/code\u003e)。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// markroot scans the i\u0026#39;th root.\n//\n// Preemption must be disabled (because this uses a gcWork).\n//\n// nowritebarrier is only advisory here.\n//\n//go:nowritebarrier\nfunc markroot(gcw *gcWork, i uint32) {\n\tbaseFlushCache := uint32(fixedRootCount) …\u003c/code\u003e\u003c/pre\u003e"
May 7, 2021
goroutine栈的申请与释放
"\u003cp\u003e对于提高对 stack 的使用效率,避免重复从heap中分配与释放,对其使用了 \u003ccode\u003epool\u003c/code\u003e 的概念,\u003ccode\u003eruntime\u003c/code\u003e 里为共提供了两个pool, 分别为 \u003ccode\u003estackpool\u003c/code\u003e ,另一个为 \u003ccode\u003estackLarge\u003c/code\u003e。\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2022/05/b33bde90901d27dd591e65c12e007fa2.png\" alt=\"\"\u003estack pool\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003estackpool\u003c/code\u003e: 16b~32k 对应通用的大小的stack。获取时通过调用 \u003ccode\u003estackpoolalloc()\u003c/code\u003e, 释放时调用 \u003ccode\u003estackpoolfree()\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003estackLarge\u003c/code\u003e:对应 \u0026gt; 32K 的 stack\u003c/p\u003e\n\u003cp\u003e在程序全局调度器 \u003ca href=\"https://github.com/golang/go/blob/go1.16.3/src/runtime/proc.go#L634\"\u003e初始化\u003c/a\u003e 时会通过调用 \u003ccode\u003estackinit()\u003c/code\u003e 实现对 \u003ccode\u003estack\u003c/code\u003e 初始化。\u003c/p\u003e\n\u003cp\u003e当我们执行一个 \u003ccode\u003ego func()\u003c/code\u003e 语句的时候,\u003ccode\u003eruntime\u003c/code\u003e 会通过调用 \u003ccode\u003enewproc()\u003c/code\u003e 函数来创建G。而内部真正创建G的函数为 \u003ccode\u003e[newproc1()](https://github.com/golang/go/blob/go1.16.3/src/runtime/proc.go#L3990-L4098)\u003c/code\u003e,在没有G可以复用的情况下,会通过 \u003ccode\u003enewg = malg(_StackMin)\u003c/code\u003e 语句创建一个\u003cstrong\u003e包含stack\u003c/strong\u003e的G。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// Allocate a …\u003c/code\u003e\u003c/pre\u003e"
May 7, 2021
Golang的GPM 模型在网络编程中存在的问题
"\u003ch2 id=\"现状\"\u003e现状\u003c/h2\u003e\n\u003cp\u003e目前在网络编程中,golang采用的是一种 \u003ccode\u003egoroutine-per-connection\u003c/code\u003e 的模式,即为每一个连接都分配一个goroutine,一个连接就是一个goroutine,多个连接之间没有关系。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;fmt\u0026#34;\n\t\u0026#34;io/ioutil\u0026#34;\n\t\u0026#34;net\u0026#34;\n\t\u0026#34;time\u0026#34;\n)\n\n//模拟server端\nfunc main() {\n\ttcpServer, _ := net.ResolveTCPAddr(\u0026#34;tcp4\u0026#34;, \u0026#34;:8080\u0026#34;)\n\tlistener, _ := net.ListenTCP(\u0026#34;tcp\u0026#34;, tcpServer)\n\n\tfor {\n\t\t//当有新客户端请求时拿到与客户端的连接\n\t\tconn, err := listener.Accept()\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\tcontinue\n\t\t}\n\n\t\t// …\u003c/code\u003e\u003c/pre\u003e"
April 30, 2021
缓存池 bytebufferpool 库实现原理
"\u003cp\u003e上一节 \u003ca href=\"https://blog.haohtml.com/archives/24697\"\u003e《Runtime: Golang 之 sync.Pool 源码分析》\u003c/a\u003e 我们介绍了sync.Pool 的源码分析,本节介绍一个 \u003ca href=\"https://github.com/valyala/fasthttp\"\u003e\u003ccode\u003efasthttp\u003c/code\u003e\u003c/a\u003e 中引用的一缓存池库 \u003ccode\u003e[bytebufferpool](https://github.com/valyala/bytebufferpool)\u003c/code\u003e,这两个库是同一个开发者。对于这个缓存池库与同类型的几个库的对比,可以参考 \u003ca href=\"https://omgnull.github.io/go-benchmark/buffer/\"\u003ehttps://omgnull.github.io/go-benchmark/buffer/\u003c/a\u003e。\u003c/p\u003e\n\u003cp\u003e建议大家了解一下\u003ccode\u003e[fasthttp](https://github.com/valyala/fasthttp)\u003c/code\u003e 这个库,性能要比直接使用内置的 \u003ccode\u003enet/http\u003c/code\u003e 高出很多,其主要原因是大量的用到了缓存池 \u003ccode\u003esync.Pool\u003c/code\u003e 进行性能提升。\u003c/p\u003e\n\u003ch2 id=\"用法\"\u003e用法\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// https://github.com/valyala/bytebufferpool/blob/18533face0/bytebuffer_example_test.go\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ebytebufferpool_test\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;fmt\u0026#34; …\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"
April 12, 2021
Golang 内存组件之mspan、mcache、mcentral 和 mheap 数据结构
"\u003cp\u003eGolang中的内存组件关系如下图所示\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/04/5a666325bb7cfea6f5182e0ee7c528cf.jpg\" alt=\"components of memory allocation\"\u003egolang 内存分配组件\u003c/p\u003e\n\u003cp\u003e在学习golang 内存时,经常会涉及几个重要的数据结构,如果不熟悉它们的情况下,理解起来就显得格外的吃力,所以本篇主要对相关的几个内存组件做下数据结构的介绍。\u003c/p\u003e\n\u003cp\u003e在 Golang 中,\u003ccode\u003emcache\u003c/code\u003e、\u003ccode\u003emspan\u003c/code\u003e、\u003ccode\u003emcentral\u003c/code\u003e 和 \u003ccode\u003emheap\u003c/code\u003e 是内存管理的四大组件,\u003ccode\u003emcache\u003c/code\u003e 管理线程在本地缓存的 \u003ccode\u003emspan\u003c/code\u003e,而 \u003ccode\u003emcentral\u003c/code\u003e 管理着全局的 \u003ccode\u003emspan\u003c/code\u003e 为所有 \u003ccode\u003emcache\u003c/code\u003e 提供所有线程。\u003c/p\u003e\n\u003cp\u003e根据分配对象的大小,内部会使用不同的内存分配机制,详细参考函数 \u003ca href=\"https://github.com/golang/go/blob/go1.16.2/src/runtime/malloc.go#L902-L1171\"\u003emallocgo()\u003c/a\u003e ,所于内存分配与回收,参考文件介绍 \u003ca href=\"https://github.com/golang/go/blob/go1.16.2/src/runtime/malloc.go#L5\"\u003emalloc.go\u003c/a\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e\u0026lt;16KB\u003c/code\u003e 会使用微小对象内存分配器从 \u003ccode\u003eP\u003c/code\u003e 中的 \u003ccode\u003emcache\u003c/code\u003e 分配,主要使用 \u003ccode\u003emcache.tinyXXX\u003c/code\u003e 这类的字段\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e16-32KB\u003c/code\u003e 从 \u003ccode\u003eP\u003c/code\u003e 中的 \u003ccode\u003emcache\u003c/code\u003e 中分配\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e\u0026gt;32KB\u003c/code\u003e 直接从 \u003ccode\u003emheap\u003c/code\u003e 中分配\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e对于golang中的内存申请流程,大家应该都非常熟悉了,这里不再进行详细描述。\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/04/1bb7fe2168b7ac2e24afadf698dc6ee6.png\" alt=\"\"\u003eGolang 内存组件关系\u003c/p\u003e\n\u003ch1 id=\"mcache\"\u003emcache\u003c/h1\u003e\n\u003cp\u003e在GPM关系中,会在每个 \u003ccode\u003eP …\u003c/code\u003e\u003c/p\u003e"
April 9, 2021
GC 对根对象扫描实现的源码分析
"\u003ch1 id=\"工作池gcwork\"\u003e工作池gcWork\u003c/h1\u003e\n\u003cp\u003e工作缓存池(\u003ccode\u003ework pool\u003c/code\u003e)实现了生产者和消费者模型,用于指向灰色对象。一个灰色对象在工作队列中被扫描标记,一个黑色对象表示已被标记不在队列中。\u003c/p\u003e\n\u003cp\u003e写屏障、根发现、栈扫描和对象扫描都会生成一个指向灰色对象的指针。扫描消费时会指向这个灰色对象,从而将先其变为黑色,再扫描它们,此时可能会产生一个新的指针指向灰色对象。这个就是三色标记法的基本知识点,应该很好理解。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003egcWork\u003c/code\u003e 是为垃圾回收器提供的一个生产和消费工作接口。\u003c/p\u003e\n\u003cp\u003e它可以用在stack上,如\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e(preemption must be disabled)\ngcw := \u0026amp;getg().m.p.ptr().gcw\n.. call gcw.put() to produce and gcw.tryGet() to consume ..\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e在标记阶段使用gcWork可以防止垃圾收集器转换到标记终止,这一点很重要,因为gcWork可能在本地持有GC工作缓冲区。可以通过禁用抢占(\u003ccode\u003esystemstack\u003c/code\u003e 或 \u003ccode\u003eacquirem\u003c/code\u003e)来实现。\u003c/p\u003e\n\u003cp\u003e数据结构\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etype gcWork struct {\n\twbuf1, wbuf2 …\u003c/code\u003e\u003c/pre\u003e"
April 6, 2021
Golang中的切片与GC
"\u003cp\u003e今天再看 \u003ccode\u003etimer\u003c/code\u003e 源码的时候,在函数 \u003ccode\u003e[clearDeletedTimers()](https://github.com/golang/go/blob/go1.16.2/src/runtime/time.go#L904-L992)\u003c/code\u003e 里看到一段对切片的处理代码,实现目的就是对一个切片内容进行缩容。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// src/runtime/time.go\n\n// The caller must have locked the timers for pp.\nfunc clearDeletedTimers(pp *p) {\n\ttimers := pp.timers\n\t......\n\t// 对无用的切片元素赋值 nil\n\tfor i := to; i \u0026lt; len(timers); i++ {\n\t\ttimers[i] = nil\n\t}\n\n\tatomic.Xadd(\u0026amp;pp.deletedTimers, -cdel)\n\tatomic.Xadd(\u0026amp;pp.numTimers, -cdel)\n\tatomic.Xadd(\u0026amp;pp.adjustTimers, -cearlier) …\u003c/code\u003e\u003c/pre\u003e"
March 29, 2021
Runtime: Golang 定时器实现原理及源码解析
"\u003cp\u003e定时器作为开发经常使用的一种数据类型,是每个开发者需要掌握的,对于一个高级开发很有必要了解它的实现原理,今天我们runtime源码来学习一下它的底层实现。\u003c/p\u003e\n\u003cp\u003e定时器分两种,分别为 Timer 和 Ticker,两者差不多,这里重点以Timer为例。\u003c/p\u003e\n\u003cp\u003e源文件位于 \u003ccode\u003e[src/time/sleep.go](https://github.com/golang/go/blob/go1.16.2/src/time/sleep.go)\u003c/code\u003e 和 \u003ccode\u003e[src/time/tick.go](https://github.com/golang/go/blob/go1.16.2/src/time/tick.go)\u003c/code\u003e 。 go version 1.16.2\u003c/p\u003e\n\u003ch1 id=\"数据结构\"\u003e数据结构\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003eTimer\u003c/code\u003e 数据结构\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// src/runtime/sleep.go\n\n// The Timer type represents a single event.\n// When the Timer expires, the current time will be sent on C,\n// unless the Timer was created by …\u003c/code\u003e\u003c/pre\u003e"
March 28, 2021
Golang中的CAS原子操作 和 锁
"\u003cp\u003e在高并发编程中,经常会出现对同一个资源并发访问修改的情况,为了保证最终结果的正确性,一般会使用 \u003ccode\u003e锁\u003c/code\u003e 和 \u003ccode\u003eCAS原子操作\u003c/code\u003e 来实现。\u003c/p\u003e\n\u003cp\u003e如要对一个变量进行计数统计,两种实现方式分别为\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;fmt\u0026#34;\n\t\u0026#34;sync\u0026#34;\n)\n\n// 锁实现方式\nfunc main() {\n\tvar count int64\n\tvar wg sync.WaitGroup\n\tvar mu sync.Mutex\n\n\tfor i := 0; i \u0026lt; 10000; i++ {\n\t\twg.Add(1)\n\t\tgo func(wg *sync.WaitGroup) {\n\t\t\tdefer wg.Done()\n\t\t\tmu.Lock()\n\t\t\tcount = count + 1\n\t\t\tmu.Unlock()\n\t\t}(\u0026amp;wg)\n\t}\n\twg.Wait()\n\n\t// count = 10000\n\tfmt.Println(\u0026#34;count = \u0026#34;, count)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e与\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport ( …\u003c/code\u003e\u003c/pre\u003e"
March 23, 2021
Golang并发同步原语之-信号量Semaphore
"\u003cp\u003e信号量是并发编程中比较常见的一种同步机制,它会保持资源计数器一直在\u003ccode\u003e0-N\u003c/code\u003e(\u003ccode\u003eN\u003c/code\u003e表示权重值大小,在用户初始化时指定)之间。当用户获取的时候会减少一点,使用完毕后再恢复过来。当遇到请求时资源不够的情况下,将会进入休眠状态以等待其它进程释放资源。\u003c/p\u003e\n\u003cp\u003e在 Golang 官方扩展库中为我们提供了一个基于权重的信号量 \u003ccode\u003e[semaphore](https://github.com/golang/sync/blob/master/semaphore/semaphore.go)\u003c/code\u003e 并发原语。\u003c/p\u003e\n\u003cp\u003e你可以将下面的参数 \u003ccode\u003en\u003c/code\u003e 理解为资源权重总和,表示每次获取时的权重;也可以理解为资源数量,表示每次获取时必须一次性获取的资源数量。为了理解方便,这里直接将其理解为资源数量。\u003c/p\u003e\n\u003ch2 id=\"数据结构\"\u003e数据结构\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003e[semaphoreWeighted](https://github.com/golang/sync/blob/master/semaphore/semaphore.go#L19-L33)\u003c/code\u003e 结构体\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etype waiter struct {\n\tn int64\n\tready chan\u0026lt;- struct{} // Closed …\u003c/code\u003e\u003c/pre\u003e"
March 22, 2021
学习Golang GC 必知的几个知识点
"\u003cp\u003e对于gc的介绍主要位于 \u003ccode\u003e[src/runtime/mgc.go](https://github.com/golang/go/blob/go1.16.2/src/runtime/mgc.go)\u003c/code\u003e,以下内容是对注释的翻译。\u003c/p\u003e\n\u003ch2 id=\"gc-四个阶段\"\u003eGC 四个阶段\u003c/h2\u003e\n\u003cp\u003e通过源文件注释得知GC共分四个阶段:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eGC 清理终止 (\u003ccode\u003eGC performs sweep termination\u003c/code\u003e)\na. \u003ccode\u003eStop the world\u003c/code\u003e, 每个P 进入GC \u003ccode\u003esafepoint\u003c/code\u003e(安全点),从此刻开始,万物静止。\nb. 清理未被清理的span,如果GC被强制执行时才会出现这些未清理的span\u003c/li\u003e\n\u003cli\u003eGC 标记阶段(\u003ccode\u003eGC performs the mark phase\u003c/code\u003e)\na. 将gc标记从 \u003ccode\u003e_GCoff\u003c/code\u003e 修改为 \u003ccode\u003e_GCmark\u003c/code\u003e,开启写屏障(\u003ccode\u003ewrite barries\u003c/code\u003e)和 协助助手(\u003ccode\u003emutator assists\u003c/code\u003e),将根对象放入队列。 在STW期间,在所有P都启用写屏障之前不会有什么对象被扫描。\nb. \u003ccode\u003eStart the world\u003c/code\u003e(恢复STW)。标记工作线程和协助助手并发的执行。对于任何指针的写操作和指针值,都会被写屏障覆盖,使新分配的对象标记为黑 …\u003c/li\u003e\u003c/ol\u003e"
March 20, 2021
Runtime: Golang 之 sync.Pool 源码分析
"\u003cp\u003ePool 指一组可以单独保存和恢复的 \u003ccode\u003e临时对象\u003c/code\u003e。Pool 中的对象随时都有可能在没有收到任何通知的情况下被GC自动销毁移除。\u003c/p\u003e\n\u003cp\u003e多个goroutine同时操作Pool是\u003ccode\u003e并发安全\u003c/code\u003e的。\u003c/p\u003e\n\u003cp\u003e源文件为 \u003ccode\u003e[src/sync/pool.go](https://github.com/golang/go/blob/master/src/sync/pool.go)\u003c/code\u003e go version: 1.16.2\u003c/p\u003e\n\u003ch1 id=\"为什么使用pool\"\u003e为什么使用Pool\u003c/h1\u003e\n\u003cp\u003e在开发高性能应用时,经常会有一些完全相同的对象需要频繁的创建和销毁,每次创建都需要在堆中分配对象,等使用完毕后,这些对象需要等待GC回收。我们知道在Golang中使用三色标记法进行垃圾回收的,在回收期间会有一个短暂\u003ccode\u003eSTW\u003c/code\u003e(stop the world)的时间段,这样就会导致程序性能下降。\u003c/p\u003e\n\u003cp\u003e那么能否实现类似数据库连接池这种效果,用来避免对象的频繁创建和销毁,达到尽可能的资源复用呢?为了实现这种需求,标准库中有了\u003ccode\u003esync.Pool\u003c/code\u003e 这个数据结构。看名字很知道它是一个池。但是它和我们想象中的数据库连接池还是有些差别的。对于数据库连接池这种资源只要不手动释放就可以一直利用, …\u003c/p\u003e"
March 19, 2021
Runtime: Golang同步原语Mutex源码分析
"\u003cp\u003e在 \u003ccode\u003esync\u003c/code\u003e 包里提供了最基本的同步原语,如互斥锁 \u003ccode\u003eMutex\u003c/code\u003e。除 \u003ccode\u003eOnce\u003c/code\u003e 和 \u003ccode\u003eWaitGroup\u003c/code\u003e 类型外,大部分是由低级库提供的,更高级别的同步最好是通过 \u003ccode\u003echannel\u003c/code\u003e 通讯来实现。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eMutex\u003c/code\u003e 类型的变量默认值是未加锁状态,在第一次使用后,此值将\u003ccode\u003e不得\u003c/code\u003e复制,这点切记!!!\u003c/p\u003e\n\u003cp\u003e本文基于go version: 1.16.2\u003c/p\u003e\n\u003cp\u003eMutex 锁实现了 \u003ccode\u003eLocker\u003c/code\u003e 接口。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// A Locker represents an object that can be locked and unlocked.\ntype Locker interface {\n\tLock()\n\tUnlock()\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"锁的模式\"\u003e锁的模式\u003c/h2\u003e\n\u003cp\u003e为了互斥公平性,Mutex 分为 \u003ccode\u003e正常模式\u003c/code\u003e 和 \u003ccode\u003e饥饿模式\u003c/code\u003e 两种。\u003c/p\u003e\n\u003ch3 id=\"正常模式\"\u003e正常模式\u003c/h3\u003e\n\u003cp\u003e在正常模式下,等待者 \u003ccode\u003ewaiter\u003c/code\u003e 会进入到一个\u003ccode\u003eFIFO\u003c/code\u003e队列,在获取锁时\u003ccode\u003ewaiter\u003c/code\u003e会按照先进先出的顺序获取。当唤醒一个\u003ccode\u003ewaiter\u003c/code\u003e 时它被并不会立即获取锁,而是要与\u003ccode\u003e新来的goroutine\u003c/code\u003e竞争,这种情况下新来的goroutine比较有优势,主要是因为它已经运行在CPU,可能它的数量还不少,所以\u003ccode\u003ewaiter\u003c/code\u003e大概率下获取不到 …\u003c/p\u003e"
March 5, 2021
Golang什么时候会触发GC
"\u003cp\u003eGolang采用了三色标记法来进行垃圾回收,那么在什么场景下会触发这个GC动作呢?\u003c/p\u003e\n\u003cp\u003e源码主要位于文件 \u003ccode\u003e[src/runtime/mgc.go](https://github.com/golang/go/blob/go1.16/src/runtime/mgc.go)\u003c/code\u003e go version 1.16\u003c/p\u003e\n\u003cp\u003e触发条件从大方面来说,分为 \u003ccode\u003e手动触发\u003c/code\u003e 和 \u003ccode\u003e系统触发\u003c/code\u003e 两种方式。手动触发一般很少用,主要通过开发者调用 \u003ccode\u003eruntime.GC()\u003c/code\u003e 函数来实现,而对于系统自动触发是 \u003ccode\u003e运行时\u003c/code\u003e 根据一些条件自行维护的,这也正是本文要介绍的内容。\u003c/p\u003e\n\u003cp\u003e不管哪种触发方式,底层回收机制是一样的,所以我们先看一下手动触发,看看能否根据它来找GC触发所需的条件。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// src/runtime/mgc.go\n\n// GC runs a garbage collection and blocks the caller until the\n// garbage collection is complete. It may also block the entire\n// program.\nfunc GC() {\n\tn := …\u003c/code\u003e\u003c/pre\u003e"
March 4, 2021
Golang 基于信号的异步抢占与处理
"\u003cp\u003e在Go1.14版本开始实现了 \u003ccode\u003e基于信号的协程抢占调度\u003c/code\u003e 模式,在此版本以前执行以下代码是永远也无法执行最后一条println语句。\u003c/p\u003e\n\u003cp\u003e本文基于go version 1.16\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n \u0026#34;runtime\u0026#34;\n \u0026#34;time\u0026#34;\n)\n\nfunc main() {\n runtime.GOMAXPROCS(1)\n go func() {\n for {\n }\n }()\n\n time.Sleep(time.Millisecond)\n println(\u0026#34;OK\u0026#34;)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e原因很简单:在main函数里只有一个CPU,从上到下执行到 \u003ccode\u003etime.Sleep()\u003c/code\u003e 函数的时候,会将 \u003ccode\u003emain goroutine\u003c/code\u003e 放入运行队列,出让了P,开始执行匿名函数,但匿名函数是一个for循环,没有任何 \u003ccode\u003eIO\u003c/code\u003e 语句,也就无法引起对 \u003ccode\u003eG\u003c/code\u003e 的调度,所以当前仅有的一个 \u003ccode\u003eP\u003c/code\u003e 永远被其占用,导致无法打印OK。\u003c/p\u003e\n\u003cp\u003e这个问题在1.14版本开始有所改变,主要是因为引入了\u003ccode\u003e基于信号的抢占模式\u003c/code\u003e。在程序启动 …\u003c/p\u003e"
March 1, 2021
Golang 的调度策略之G的窃取
"\u003cp\u003e我们上篇文章( \u003ca href=\"https://blog.haohtml.com/archives/21411\"\u003eGolang 的底层引导流程/启动顺序\u003c/a\u003e)介绍了一个golang程序的启动流程,在文章的最后对于最重要的一点“\u003ccode\u003e调度\u003c/code\u003e“ (函数 \u003ccode\u003e[schedule()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L2607-L2723)\u003c/code\u003e) 并没有展开来讲,今天我们继续从源码来分析一下它的调度机制。\u003c/p\u003e\n\u003cp\u003e在此之前我们要明白golang中的调度主要指的是什么?在 \u003ccode\u003esrc/runtime/proc.go\u003c/code\u003e 文件里有一段注释这样写到\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e// Goroutine scheduler\u003c/p\u003e\n\u003cp\u003e// The scheduler’s job is to distribute ready-to-run goroutines over worker threads.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003e这里指如何找一个已准备好运行的 G 关联到PM 让其执行。对于G 的调度可以围绕三个方面来理解:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e时机:什么时候关联(调度)。对于调度时机一般是指有空闲P的时候都会去找G执行\u003c/li\u003e\n\u003cli\u003e对象:选择哪个G进行调度。这是我们本篇要讲的内容\u003c/li\u003e\n\u003cli\u003e机制:如何调度。\u003ccode\u003eexecute()\u003c/code\u003e 函数\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e理解 …\u003c/p\u003e"
February 27, 2021
Runtime: Golang是如何处理系统调用阻塞的?
"\u003cp\u003e我们知道在Golang中,当一个Goroutine由于执行 \u003ccode\u003e系统调用\u003c/code\u003e 而阻塞时,会将M从GPM中分离出去,然后P再找一个G和M重新执行,避免浪费CPU资源,那么在内部又是如何实现的呢?今天我们还是通过学习Runtime源码的形式来看下他的内部实现细节有哪些?\u003c/p\u003e\n\u003cp\u003ego version 1.15.6\u003c/p\u003e\n\u003cp\u003e我们知道一个P有四种运行状态,而当执行系统调用函数阻塞时,会从 \u003ccode\u003e_Prunning\u003c/code\u003e 状态切换到 \u003ccode\u003e_Psyscall\u003c/code\u003e,等系统调用函数执行完毕后再切换回来。\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/01/0d20dfce0e3dd6968aebe84535b853c6.png\" alt=\"P的状态切换\"\u003eP的状态切换\u003c/p\u003e\n\u003cp\u003e从上图我们可以看出 \u003ccode\u003eP\u003c/code\u003e 执行系统调用时会执行 \u003ccode\u003e[entersyscall()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3134-L3142)\u003c/code\u003e 函数(另还有一个类似的阻塞函数 \u003ca href=\"https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3171-L3212\"\u003e\u003ccode\u003eentersyscallblock()\u003c/code\u003e\u003c/a\u003e ,注意两者的区别)。当系统调用执行完毕切换回去会执行 \u003ca href=\"https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3222-L3305\"\u003e\u003ccode\u003eexitsyscall()\u003c/code\u003e\u003c/a\u003e 函数,下面我们看一下这两个函数的实现。\u003c/p\u003e\n\u003ch1 id=\"进入系统调用\"\u003e进入系统调用\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// Standard syscall entry used by the go syscall …\u003c/code\u003e\u003c/pre\u003e"
February 26, 2021
Runtime: 当一个goroutine 运行结束后会发生什么
"\u003cp\u003e上一篇我们介绍了 \u003ca href=\"https://blog.haohtml.com/archives/23168\"\u003e创建一个goroutine 会经历些什么\u003c/a\u003e,今天我们再看下当一个\u003ccode\u003egoroutine\u003c/code\u003e 运行结束的时候,又会发生什么?\u003c/p\u003e\n\u003cp\u003ego version 1.15.6。\u003c/p\u003e\n\u003cp\u003e主要源文件为 \u003ccode\u003e[src/runtime/proc.go](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go)\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e当一个\u003ccode\u003egoroutine\u003c/code\u003e 运行结束的时候,默认会执行一个 \u003ccode\u003e[goexit1()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L2941-L2950)\u003c/code\u003e 的函数,这是一个只有八行代码的函数,其中最后以通过 \u003ccode\u003e[mcall()](https://github.com/golang/go/blob/go1.15.6/src/runtime/stubs.go#L34)\u003c/code\u003e 调用 \u003ccode\u003e[goexit0](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L2952-L3011)\u003c/code\u003e 函数结束。因此我们主 …\u003c/p\u003e"
February 17, 2021
Runtime: 创建一个goroutine都经历了什么?
"\u003cp\u003e我们都知道goroutine的在golang中发挥了很大的作用,那么当我们创建一个新的goroutine时,它是怎么一步一步创建的呢?都经历了哪些操作呢?今天我们通过源码来剖析一下创建goroutine都经历了些什么?go version 1.15.6\u003c/p\u003e\n\u003cp\u003e对goroutine最关键的两个函数是 \u003ccode\u003e[newproc()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3535-L3564)\u003c/code\u003e 和 \u003ccode\u003e[newproc1()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3566-L3674)\u003c/code\u003e,而 \u003ccode\u003enewproc1()\u003c/code\u003e 函数是我们最需要关注的。\u003c/p\u003e\n\u003ch2 id=\"函数-newproc\"\u003e函数 newproc()\u003c/h2\u003e\n\u003cp\u003e我们先看一个简单的创建goroutine的例子,找出来创建它的函数。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nfunc start(a, b, c int64) {\n\t_ = a + b + c\n}\n\nfunc main() {\n\tgo start(7, 2, 5)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e输出结果: …\u003c/p\u003e"
February 15, 2021
Runtime: 理解Golang中接口interface的底层实现
"\u003cp\u003e接口类型是Golang中是一种非常非常常见的\u003ccode\u003e数据类型\u003c/code\u003e,每个开发人员都很有必要知道它到底是如何使用的,如果了解了它的底层实现就对开发就更有帮助了。\u003c/p\u003e\n\u003ch1 id=\"接口的定义\"\u003e接口的定义\u003c/h1\u003e\n\u003cp\u003e在Golang中 \u003ccode\u003einterface\u003c/code\u003e 通常是指实现了一 组抽象方法的集合,它提供了一种无侵入式的方式。当你实现了一个接口中指定的所有方法的时候,那么就实现了这个接口,在Golang中对它的实现并不需要 \u003ccode\u003eimplements\u003c/code\u003e 关键字。\u003c/p\u003e\n\u003cp\u003e有时候我们称这种模型叫做鸭子模型(Duck typing),维基百科对鸭子模型的定义是\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e”If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.“\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003e翻译过来就是 ”如果它看起来像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那他就可以认为是鸭子“。\u003c/p\u003e\n\u003cp\u003eGo 不同版本之间interface的结构可能不太一样,但整体都差不多,这里使用的Go版本为 1.15.6。\u003c/p\u003e\n\u003ch1 id=\"数据结构\"\u003e数据结构\u003c/h1\u003e\n\u003cp\u003eGo 中 \u003ccode\u003einterface\u003c/code\u003e 在运行时可分 \u003ccode\u003eeface\u003c/code\u003e 和 \u003ccode\u003eiface\u003c/code\u003e 两种数据结构,我们先看一下对它们的 …\u003c/p\u003e"
February 13, 2021
认识Golang中的sysmon监控线程
"\u003cp\u003eGo Runtime 在启动程序的时候,会创建一个独立的 \u003ccode\u003eM\u003c/code\u003e 作为监控线程,称为 \u003ccode\u003esysmon\u003c/code\u003e,它是一个系统级的 \u003ccode\u003edaemon\u003c/code\u003e 线程。这个\u003ccode\u003esysmon\u003c/code\u003e 独立于 GPM 之外,也就是说不需要P就可以运行,因此官方工具 \u003ccode\u003ego tool trace\u003c/code\u003e 是无法追踪分析到此线程( \u003ca href=\"https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L4639-L4760\"\u003e源码\u003c/a\u003e)。\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/02/6ad0cfb3df2281110cf60630fcfb0e96.png\" alt=\"\"\u003esysmon\u003c/p\u003e\n\u003cp\u003e在程序执行期间 \u003ccode\u003esysmon\u003c/code\u003e 每隔 \u003ccode\u003e20us~10ms\u003c/code\u003e 轮询执行一次( \u003ca href=\"https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L4652-L4659\"\u003e源码\u003c/a\u003e),监控那些长时间运行的 G 任务, 然后设置其可以被强占的标识符,这样别的 \u003ccode\u003eGoroutine\u003c/code\u003e 就可以抢先进来执行。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// src/runtime/proc.go\n\n// forcegcperiod is the maximum time in nanoseconds between garbage\n// collections. If we go this long without a garbage collection, one\n// is forced to run.\n//\n// This is a variable for testing purposes. It normally doesn\u0026#39;t …\u003c/code\u003e\u003c/pre\u003e"
February 11, 2021
g0 特殊的goroutine
"\u003cp\u003e在上篇 \u003ca href=\"https://blog.haohtml.com/archives/21010\"\u003e《golang中G、P、M 和 sched 三者的数据结构》\u003c/a\u003e文章中,我们介绍了\u003ccode\u003eG\u003c/code\u003e、\u003ccode\u003eM\u003c/code\u003e 和 \u003ccode\u003eP\u003c/code\u003e 的数据结构,其中M结构体中第一个字段是 \u003ccode\u003eg0\u003c/code\u003e,这个字段也是一个 \u003ccode\u003egoroutine\u003c/code\u003e,但和普通的 \u003ccode\u003egoroutine\u003c/code\u003e 有一些区别,它主要用来实现对 goroutine 进行调度,下面我们将介绍它是如何实现调度goroutine的。\u003c/p\u003e\n\u003cp\u003e另外还有一个 \u003ccode\u003em0\u003c/code\u003e , 它是一个全局变量,与 \u003ccode\u003eg0\u003c/code\u003e 的区别如下\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/03/8e229b7806870bf4f17da207665b8a43.jpg\" alt=\"\"\u003eM0 与 g0的区别\u003c/p\u003e\n\u003cp\u003e本文主要翻译自 \u003ca href=\"https://medium.com/a-journey-with-go/go-g0-special-goroutine-8c778c6704d8\"\u003eGo: g0, Special Goroutine\u003c/a\u003e 一文,有兴趣的可以查阅原文,作者有一系列高质量的文章推荐大家都阅读一遍。ℹ️ 本文基于 Go 1.13。\u003c/p\u003e\n\u003cp\u003e我们知道在Golang中所有的\u003ccode\u003egoroutine\u003c/code\u003e的运行都是由\u003ccode\u003e调度器\u003c/code\u003e来负责管理的,go调度器尝试为所有的\u003ccode\u003egoroutine\u003c/code\u003e来分配运行时间,当有\u003ccode\u003egoroutine\u003c/code\u003e被阻塞或终止时,调度器会通过对\u003ccode\u003egoroutine\u003c/code\u003e 进行调度以此来保证所有CPU都处于忙碌状态,避免有CPU空闲状态浪费时间。\u003c/p\u003e\n\u003ch2 id=\"goroutine-切换规则\"\u003egoroutine 切换规则\u003c/h2\u003e\n\u003cp\u003e在此之前我们需要记住一些goroutine切换规则。runtime源码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// …\u003c/code\u003e\u003c/pre\u003e"
January 26, 2021
Golang环境变量之GODEBUG
"\u003cp\u003e\u003ccode\u003eGODEBUG\u003c/code\u003e 是 golang中一个控制runtime调度变量的变量,其值为一个用逗号隔开的 name=val对列表,常见有以下几个命名变量。\u003c/p\u003e\n\u003ch2 id=\"allocfreetrace\"\u003eallocfreetrace\u003c/h2\u003e\n\u003cp\u003e设置\u003ccode\u003eallocfreetrace = 1\u003c/code\u003e会导致对每个分配进行概要分析,并在每个对象的分配上打印堆栈跟踪并释放它们。\u003c/p\u003e\n\u003ch2 id=\"clobberfree\"\u003eclobberfree\u003c/h2\u003e\n\u003cp\u003e设置 \u003ccode\u003eclobberfree=1\u003c/code\u003e会使垃圾回收器在释放对象的时候,对象里的内存内容可能是错误的。\u003c/p\u003e\n\u003ch2 id=\"cgocheck\"\u003ecgocheck\u003c/h2\u003e\n\u003cp\u003ecgo相关。\u003c/p\u003e\n\u003cp\u003e设置 \u003ccode\u003ecgocheck=0\u003c/code\u003e 将禁用当包使用cgo非法传递给go指针到非go代码的检查。如果值为1(默认值)会启用检测,但可能会丢失有一些错误。如果设置为2的话,则不会丢失错误。但会使程序变慢。\u003c/p\u003e\n\u003ch2 id=\"efence\"\u003eefence\u003c/h2\u003e\n\u003cp\u003e设置 \u003ccode\u003eefence=1\u003c/code\u003e会使回收器运行在一个模式。每个对象都在一个唯一的页和地址,且永远也不会被回收。\u003c/p\u003e\n\u003ch2 id=\"gccheckmark\"\u003egccheckmark\u003c/h2\u003e\n\u003cp\u003eGC相关。\u003c/p\u003e\n\u003cp\u003e设置 \u003ccode\u003egccheckmark=1\u003c/code\u003e 启用验证垃圾回收器的并发标记,通过在STW时第二个标记阶段来实现,如果在第二阶段的时候,找到一个可达对象,但未找到并发标记,则GC会发生Panic。\u003c/p\u003e\n\u003ch2 id=\"gcpacertrace\"\u003egcpacertrace …\u003c/h2\u003e"
January 26, 2021
Golang中MemStats的介绍
"\u003cp\u003e平时在开发中,有时间需要通过查看内存使用情况来分析程序的性能问题,经常会使用到 MemStats 这个结构体。但平时用到的都是一些最基本的方法,今天我们全面认识一下MemStas。\u003c/p\u003e\n\u003cp\u003e相关文件为 \u003ccode\u003esrc/runtime/mstats.go\u003c/code\u003e ,本文章里主要是与内存统计相关。\u003c/p\u003e\n\u003ch2 id=\"memstats-结构体\"\u003eMemStats 结构体\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// MemStats记录有关内存分配器的统计信息\ntype MemStats struct {\n\t// General statistics.\n\tAlloc uint64\n\tTotalAlloc uint64\n\tSys uint64\n\tLookups uint64\n\tMallocs uint64\n\tFrees uint64\n\n\t// Heap memory statistics.\n\tHeapAlloc uint64\n\tHeapSys uint64\n\tHeapIdle uint64\n\tHeapInuse uint64\n\tHeapReleased uint64\n\tHeapObjects uint64\n\n\t// Stack memory statistics.\n\tStackInuse uint64 …\u003c/code\u003e\u003c/pre\u003e"
January 25, 2021
Golang中Stack的管理
"\u003ch1 id=\"栈的演变\"\u003e栈的演变\u003c/h1\u003e\n\u003cp\u003e在 Go1.13之前的版本,Golang 栈管理是使用的\u003ccode\u003e分段栈(Segment Stacks)\u003c/code\u003e机制来实现的,由于sgement stack 存在 \u003ccode\u003e热分裂(hot split\u003c/code\u003e)的问题,后面版本改为采用\u003ccode\u003e连续栈( [Contiguous stacks](https://docs.google.com/document/d/1wAaf1rYoM4S4gtnPh0zOlGzWtrZFQ5suE8qr2sD8uWQ/pub))\u003c/code\u003e机制( \u003ca href=\"https://golang.org/doc/go1.3#stacks\"\u003e说明\u003c/a\u003e)。\u003c/p\u003e\n\u003ch2 id=\"分段栈segment-stack\"\u003e分段栈(Segment Stack)\u003c/h2\u003e\n\u003cp\u003e分段栈是指开始时只有一个stack,当需要更多的 stack 时,就再去申请一个,然后将多个stack 之间用双向链接连接在一起。当使用完成后,再将无用的 \u003ccode\u003estack\u003c/code\u003e 从链接中删除释放内存。\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/01/d2b5ca33bd970f64a6301fa75ae2eb22-3.png\" alt=\"\"\u003esegment stack\u003c/p\u003e\n\u003cp\u003e可以看到这样确实实现了stack 按需增长和收缩,在增加新stack时不需要拷贝原来的数据,系统使用率挺高的。但在一定特别的情况下会存在 \u003ccode\u003e热分裂(hot split)\u003c/code\u003e 的问题。\u003c/p\u003e\n\u003cp\u003e当一个 stack 即将用完的时候,任意一个函数都会导致堆栈的扩容,当函数执行完返回后,又要触发堆栈的收缩。如果这个操作 …\u003c/p\u003e"
January 22, 2021
Golang 的底层引导流程/启动顺序
"\u003cp\u003e在Golang中,程序的执行入口为 \u003ccode\u003emain()\u003c/code\u003e 函数,那么底层又是如何工作的呢? 这个问题的答案我们可以在runtime源码找到。对它的解释主要在 \u003ccode\u003e[src/runtime/proc.go](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go)\u003c/code\u003e 文件,下面我们看一下它是如何一步一步开始执行的。go version 1.15.6\u003c/p\u003e\n\u003cp\u003e在文件头部有一段对 \u003ccode\u003e[Goroutine scheduler](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L19)\u003c/code\u003e 的介绍,我们先了解一下。\u003c/p\u003e\n\u003cp\u003e调度器的工作是分发\u003ccode\u003egoroutines\u003c/code\u003e到工作线程让其运行。一句话指明了调度器的存在意义,就是指挥协调GPM干活。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e主要包含三部分\u003c/strong\u003e\n\u003ccode\u003eG\u003c/code\u003e 指的是 goroutine\n\u003ccode\u003eM\u003c/code\u003e 工作线程,也叫\u003ccode\u003emachine\u003c/code\u003e\n\u003ccode\u003eP\u003c/code\u003e 处理器(逻辑CPU),执行 \u003ccode\u003eGo code\u003c/code\u003e 的一种资源。这里的Go code 其实就是 goroutine里的代码。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eM\u003c/code\u003e必须被指派给\u003ccode\u003eP\u003c/code\u003e去执行 \u003ccode\u003eGo code\u003c/code\u003e, 但可以被 …\u003c/p\u003e"
January 21, 2021
golang中G、P、M 和 sched 三者的数据结构
"\u003cp\u003eG、P、M 三者是golang实现高并发能的最为重要的概念,\u003ccode\u003eruntime\u003c/code\u003e 通过 \u003ccode\u003e调度器\u003c/code\u003e 来实现三者的相互调度执行,通过 \u003ccode\u003ep\u003c/code\u003e 将用户态的 \u003ccode\u003eg\u003c/code\u003e 与内核态资源 \u003ccode\u003em\u003c/code\u003e 的动态绑定来执行,以减少以前通过频繁创建内核态线程而产生的一系列的性能问题,充分发挥服务器最大有限资源。\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/01/7c68d000148bf601267b43631c795bfd.png\" alt=\"\"\u003eGPM 协作\u003c/p\u003e\n\u003cp\u003e调度器的工作是将一个 G(需要执行的代码)、一个 M(代码执行的地方)和一个 P(代码执行所需要的权限和资源)结合起来。\u003c/p\u003e\n\u003cp\u003e所有的 g、m 和 p 对象都是分配在\u003ccode\u003e堆\u003c/code\u003e上且永不释放的,所以它们的内存使用是很稳定的。得益于此,runtime 可以在调度器实现中避免写屏障。当一个G执行完成后,可以放入pool中被再次使用,避免重复申请资源。\u003c/p\u003e\n\u003cp\u003e本节主要通过阅读runtime源码来认识这三个组件到底长的是什么样子,以此加深对 GPM 的理解。go version go1.15.6\u003c/p\u003e\n\u003cp\u003e理解下文前建议先阅读一下 \u003ccode\u003esrc/runtime/HACKING.md\u003c/code\u003e 文件,中文可阅读 \u003ca href=\"https://www.purewhite.io/2019/11/28/runtime-hacking-translate/\"\u003e这里\u003c/a\u003e,这个文件内容是面向开发者理解\u003ccode\u003eruntime\u003c/code\u003e的很值得看一看。\u003c/p\u003e\n\u003cp\u003e本文若没有指定源码文件路径,则默认为 \u003ccode\u003esrc/runtime/runtime2.go\u003c/code\u003e。\u003c/p\u003e\n\u003ch1 id=\"g\"\u003eG\u003c/h1\u003e\n\u003cp\u003eG …\u003c/p\u003e"
January 18, 2021
Runtime: Golang中channel实现原理源码分析
"\u003cp\u003echannel是golang中特有的一种数据结构,通常与goroutine一起使用,下面我们就介绍一下这种数据结构。\u003c/p\u003e\n\u003ch2 id=\"channel数据结构\"\u003echannel数据结构\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003echannel\u003c/code\u003e 是Golang 中最重要的一个数据结构,源码里对应的结构体是\u003ccode\u003ehchan\u003c/code\u003e,当我们创建一个\u003ccode\u003echannel\u003c/code\u003e 的时候,实际上是创建了一个\u003ccode\u003ehchan\u003c/code\u003e结构体。\u003c/p\u003e\n\u003ch3 id=\"hchan结构体\"\u003ehchan结构体\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// src/runtime/chan.go\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ehchan\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eqcount\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// total data in the queue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003edataqsiz\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// size of the circular queue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003ebuf\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eunsafe\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePointer\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// points to an array of dataqsiz elements\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eelemsize\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint16\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eclosed\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint32\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eelemtype\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e*\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003e_type\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// element type\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003esendx\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// send index\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003erecvx\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003euint\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e// …\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"
January 11, 2021
Runtime:源码解析Golang 的map实现原理
"\u003cp\u003ego version 1.15.6\u003c/p\u003e\n\u003cp\u003emap作为一种常见的 \u003ccode\u003ekey-value\u003c/code\u003e 数据结构,不同语言的实现原理基本差不多。首先在系统里分配一段连接的内存地址作为数组,然后通过对map键进行\u003ccode\u003ehash算法\u003c/code\u003e(最终将键转换成了一个整型数字)定位到不同的桶bucket(数组的索引位置),然后将值存储到对应的bucket里\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/01/7614fd6c619c3d1b07a787a82b19cad0.png\" alt=\"map hash算法\"\u003e\u003c/p\u003e\n\u003cp\u003e理想的情况下是一个\u003ccode\u003ebucket\u003c/code\u003e存储一个值,即数组的形式,时间复杂度为O(1)。\u003c/p\u003e\n\u003cp\u003e如果存在键值碰撞的话,可以通过 \u003ccode\u003e链表法\u003c/code\u003e 或者 \u003ccode\u003e开放寻址法\u003c/code\u003e 来解决。\u003c/p\u003e\n\u003cp\u003e链表法\u003cimg src=\"https://blogstatic.haohtml.com/uploads/2021/01/57c223fe323d13755f7b47d3ad427fe1-1.png\" alt=\"d2b5ca33bd970f64a6301fa75ae2eb22-1\"\u003e\u003c/p\u003e\n\u003cp\u003e开放寻址法\u003c/p\u003e\n\u003cp\u003e对于开放寻址法有多种算法,常见的有线性探测法,线性补偿探测法,随机探测法等,这里不再介绍。\u003c/p\u003e\n\u003ch2 id=\"map基本数据结构\"\u003emap基本数据结构\u003c/h2\u003e\n\u003ch3 id=\"hmap结构体\"\u003ehmap结构体\u003c/h3\u003e\n\u003cp\u003emap的核心数据结构定义在 \u003ccode\u003e/runtime/map.go\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// A header for a Go map.\ntype hmap struct {\n\t// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.\n\t// Make sure this stays in sync …\u003c/code\u003e\u003c/pre\u003e"
December 26, 2020
Golang并发模式之扇入FAN-IN和扇出FAN-OUT
"\u003cp\u003e在现实世界中,经常有一些工作是属于流水线类型的,它们的每一个步骤都是紧密关联的,第一步先做什么,再做什么,最后做什么。特别是制造业这个行业,基本全是流水线生产车间。在我们开发中也经常遇到这类的业务场景。\u003c/p\u003e\n\u003cp\u003e假如我们有个流水线共分三个步骤,分别是 job1、job2和job3。代码: \u003ca href=\"https://play.golang.org/p/e7ZlP9ofXB3\"\u003ehttps://play.golang.org/p/e7ZlP9ofXB3\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;fmt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;time\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ejob1\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ecount\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e) \u003cspan style=\"color:#f92672\"\u003e\u0026lt;-\u003c/span\u003e\u003cspan style=\"color:#66d9ef\"\u003echan\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#a6e22e\"\u003eoutCh\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e make(\u003cspan style=\"color:#66d9ef\"\u003echan\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ego\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003edefer\u003c/span\u003e close(\u003cspan style=\"color:#a6e22e\"\u003eoutCh\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\u003cspan style=\"color:#66d9ef\"\u003efor\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e \u0026lt; \u003cspan style=\"color:#a6e22e\"\u003ecount\u003c/span\u003e; \u003cspan style=\"color:#a6e22e\"\u003ei\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e++\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003etime\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSleep\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003etime\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eSecond\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003efmt\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePrintln\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;job1 finish:\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t\t\u003cspan style=\"color:#a6e22e\"\u003eoutCh\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e\u0026lt;-\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\t}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t}()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t\u003cspan style=\"color:#66d9ef\"\u003ereturn\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eoutCh\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ejob2\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003einCh …\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"
December 24, 2020
重新认识Golang中的空结构体
"\u003col\u003e\n\u003cli\u003e认识空结构体\u003c/li\u003e\n\u003cli\u003e低层实现原理\u003c/li\u003e\n\u003cli\u003e空结构体之内存对齐\u003c/li\u003e\n\u003cli\u003e应用场景\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e在golang中,如果我们想实现一个set集合的话,一般会使用map来实现,其中将set的值作为map的键,对于map的值一般使用一个空结构体来实现,当然对map值也可以使用一个bool类型或者数字类型等,只要符合一个键值对应关系即可。但我们一般推荐使用struct{}来实现,为什么呢?\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport \u0026#34;fmt\u0026#34;\n\nfunc main() {\n\tm := make(map[int]struct{})\n\tm[1] = struct{}{}\n\tm[2] = struct{}{}\n\n\tif _, ok := m[1]; ok {\n\t\tfmt.Println(\u0026#34;exists\u0026#34;)\n\t}\n\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e上面这段代码是一个很简单的使用map实现的set功能,这里是采用空结构体struct{}来实现。\u003c/p\u003e\n\u003cp\u003e在分析为什么使用struct{}以前,我看先认识一个struct。\u003c/p\u003e\n\u003ch2 id=\"认识空结构体-struct\"\u003e认识空结构体 struct\u003c/h2\u003e\n\u003cp\u003e我们先看一个这段代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport ( …\u003c/code\u003e\u003c/pre\u003e"
December 17, 2020
Golang中的内存重排(Memory Reordering)
"\u003ch2 id=\"什么是内存重排\"\u003e什么是内存重排\u003c/h2\u003e\n\u003cp\u003e内存重排指的是内存的读/写指令重排。\u003c/p\u003e\n\u003ch2 id=\"为什么要内存重排\"\u003e为什么要内存重排\u003c/h2\u003e\n\u003cp\u003e为了提升程序执行效率,减少一些IO操作,一些硬件或者编译器会对程序进行一些指令优化,优化后的结果可能会导致程序编码时的顺序与代码编译后的先后顺序不一致。\u003c/p\u003e\n\u003cp\u003e就拿做饭场景来说吧,是先蒸米还是先炒菜,这两者是没有冲突的,编译器在编译时有可能与你要求的顺序不一样。\u003c/p\u003e\n\u003ch2 id=\"编译器重排\"\u003e编译器重排\u003c/h2\u003e\n\u003cp\u003e如下面这段代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eX = 0\nfor i in range(100):\n X = 1\n print X\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e要实现打印100次1,很显示在for里面每次都执行X=1语句有些浪费资源,如果将初始变量值修改为1,是不是要快的多。编译器也分析到了这一点,于是在编译时对代码做了以下优化\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eX = 1\nfor i in range(100):\n print X\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e最终输出结果是一样的,两段代码功能也一样。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e但是\u003c/strong\u003e如果此时有另一个线程里执行了一个 X=0 的赋值语句的话(两个线程同时运行),那么输出结果就可能与我们想要的不一样了。\u003c/p\u003e\n\u003cp\u003e优化前情况:第一个线程执行到了第3次print X 后,第二个线程执行了X=0,把X 的值进行了修改,结果就有可能是1110 …\u003c/p\u003e"
November 19, 2020
Golang中的并发原语 Singleflight
"\u003cp\u003e在Golang中有一个并发原语是 \u003ca href=\"https://pkg.go.dev/golang.org/x/sync/singleflight\"\u003eSingleflight\u003c/a\u003e,好像知道的开发者并不多。其中著名的 \u003ca href=\"https://github.com/golang/groupcache\"\u003ehttps://github.com/golang/groupcache\u003c/a\u003e 就用到了这个并发原语。\u003c/p\u003e\n\u003ch2 id=\"golang版本\"\u003eGolang版本\u003c/h2\u003e\n\u003cp\u003ego1.15.5\u003c/p\u003e\n\u003ch2 id=\"相关知识点\"\u003e相关知识点\u003c/h2\u003e\n\u003cp\u003emap、Mutex、channel、\u003c/p\u003e\n\u003ch2 id=\"使用场景\"\u003e使用场景\u003c/h2\u003e\n\u003cp\u003e一般用在对指定资源频繁操作的情况下,如高并发下的“缓存击穿”问题。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e缓存击穿:一个存在的key,在缓存过期的瞬间,同时有大量的请求过来,造成所有请求都去DB读取数据,这些请求都会击穿缓存到DB,造成瞬时DB请求量大、压力瞬间骤增,导致数据库负载过高,影响整个系统正常运行。(缓存击穿不同于 缓存雪崩 和 缓存穿透)\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003e怎么理解这个原语呢,简单的讲就是将对同一个资源的多个请求合并为一个请求。\u003c/p\u003e\n\u003cp\u003e举例说明,假如当有10万个请求来获取同一个key的值的时候,正常情况下会执行10万次get操作。而使用singleflight并发语后,只需要首次的地个请求执行一次get操作就可以了,其它请求再过来时,只需要只需要等待即可。待执行结果返回后,再把结果分别返回给等待中的请求,每个请求再返回给客户端,由此看看,在一定的高 …\u003c/p\u003e"
September 19, 2020
Golang开发中中使用GitHub私有仓库
"\u003cp\u003e私有仓库地址为\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egithub.com/cfanbo/websocket\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"一设置私有环境变量-goprivate\"\u003e一、设置私有环境变量 GOPRIVATE\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ go env -w GOPRIVATE=github.com/cfanbo/websocket\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e对于为什么需要设置 GOPRIMARY 变量,可以参考 \u003ca href=\"https://gocn.vip/topics/9904\"\u003e这里\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e对于GOPRIVATE值级别分为仓库级别和账号级别。\u003c/p\u003e\n\u003cp\u003e如果只有一个仓库,直接设置为仓库地址即可。如果有多个私有仓库的话,使用”,”分开,都在这个账号下,也可以将值设置为账号级别,这样账号下的所有私有仓库都可以正常访问。如 \u003ca href=\"http://github.com/cfanbo\"\u003ehttp://github.com/cfanbo\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e如果不想每次都重新设置,我们也可以利用通配符,例如:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ go env -w GOPRIVATE=\u0026#34;*.example.com\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e这样子设置的话,所有模块路径为 example.com 的子域名(例如:git.example.com)都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 example.com 本身。\u003c/p\u003e\n\u003cp\u003e国内用户访问仓库建议设置 GORPOXY …\u003c/p\u003e"
May 27, 2020
golang中几种对goroutine的控制方法
"\u003cp\u003e我们先看一段代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunc listen() {\n\tticker := time.NewTicker(time.Second)\n\tfor {\n\t\tselect {\n\t\tcase \u0026lt;-ticker.C:\n\t\t\tfmt.Println(time.Now())\n\t\t}\n\t}\n}\nfunc main() {\n\tgo listen()\n\ttime.Sleep(time.Second * 5)\n\tfmt.Println(\u0026#34;main exit\u0026#34;)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e非常简单的一个goroutine用法,想必每个gopher都看过的。\u003c/p\u003e\n\u003cp\u003e不过在实际生产中,我们几乎看不到这种用法的的身影,原因很简单,我们无法实现对goroutine的控制,而一般业务中我们需要根据不同情况对goroutine进行各种操作。\u003c/p\u003e\n\u003cp\u003e要实现对goroutine的控制,一般有以下两种。\u003c/p\u003e\n\u003ch2 id=\"一手动发送goroutine控制信号\"\u003e一、手动发送goroutine控制信号\u003c/h2\u003e\n\u003cp\u003e这里我们发送一个退出goroutine的信号。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// listen 利用只读chan控制goroutine的退出\nfunc listen(ch \u0026lt;-chan bool) {\n\tticker := …\u003c/code\u003e\u003c/pre\u003e"
May 3, 2020
Golang遍历切片删除元素引起恐慌问题
"\u003cp\u003e删除一个切片的部分元素, 告知切片操作:\u003ca href=\"http://cn.voidcc.com/question/p-mkbvfagj-hy.html\"\u003eGolang遍历切片恐慌时删除元素\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"问题描述\"\u003e问题描述\u003c/h2\u003e\n\u003cp\u003e代码( \u003ca href=\"https://go.dev/play/p/Kyvo7YQuw1m\"\u003e演示代码\u003c/a\u003e):\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;fmt\u0026#34;\n)\n\nfunc main() {\n\tslice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}\n\tfor i, value := range slice {\n\t\tif value%3 == 0 { // remove 3, 6, 9\n\t\t\tslice = append(slice[:i], slice[i+1:]...)\n\t\t}\n\t}\n\tfmt.Printf(\u0026#34;%v\u0026#34;, slice)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e运行结果\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epanic: runtime error: slice bounds out of range [8:6]\n\ngoroutine 1 [running]:\nmain.main()\n\t/tmp/sandbox2635969259/prog.go:11 +0x212\n\nProgram exited.\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"解决办法\"\u003e\u003cstrong\u003e解决办法:\u003c/strong\u003e\u003c/h2\u003e\n\u003cp\u003e以下是网友想到的几种办法\u003c/p\u003e\n\u003cp\u003e1、使用\u003ccode\u003egoto\u003c/code\u003e和标签\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunc …\u003c/code\u003e\u003c/pre\u003e"
April 30, 2020
Golang中select用法导致CPU占用100%的问题分析
"\u003cp\u003e上一节( \u003ca href=\"https://blog.haohtml.com/archives/19670\"\u003egolang中有关select的几个知识点\u003c/a\u003e)中介绍了一些对于select{}的一些用法,今天介绍一下有关select在 \u003ccode\u003efor语句\u003c/code\u003e 中由于使用不当引起的CPU占用100% 的案例。\u003c/p\u003e\n\u003cp\u003e先看代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;fmt\u0026#34;\n\t\u0026#34;time\u0026#34;\n)\n\nfunc main() {\n\tch := make(chan int, 10)\n\t// 读取chan\n\tgo func() {\n\t\tfor {\n\t\t\tselect {\n\t\t\tcase i := \u0026lt;-ch:\n\t\t\t\t// 只读取15次chan\n\t\t\t\tfmt.Println(i)\n\t\t\tdefault:\n\t\t\t\t// 读取15次chan以后的操作一直在这个空语句无任何IO操作的default条件里死循环,无法出让P,以保证一个GPM关系。\n\t\t\t\t// 而如果无default条件的话,则系统当读取完15次chan后,当前goroutine会发生 chan IO 阻塞, Go调度器根据GPM的调度关系,会将当前执行关系中的G切换出去,再从LRQ队列中取一个新的G,重新组成一 …\u003c/code\u003e\u003c/pre\u003e"
April 21, 2020
基于 GitHub Actions 实现 Golang 项目的自动构建部署
"\u003cp\u003e前几天 GitHub官网宣布 GitHub 的所有核心功能对所有人都免费开放,不得不说自从微软收购了GitHub后,确实带来了一些很大的改变。\u003c/p\u003e\n\u003cp\u003e以前有些项目考虑到协作关系的原因,虽然放在github上面,但对于一些项目的持续构建和部署一般是通过自行抢建Travis CI、jenkins等系统来实现。虽然去年推出了Actions用来代替它类三方系统,但感觉着还是不方便,必须有些核心功能无法使用,此消息的发布很有可能将这种格局打破。\u003c/p\u003e\n\u003cp\u003e本篇教程将介绍使用github的系列产品来实现项目的发布,构建,测试和部署,当然这仅仅是一个非常小的示例,有些地方后期可能会有更好的瞿恩方案。\u003c/p\u003e\n\u003cp\u003eGitHub Actions 是一款持续集成工具,包括clone代码,代码构建,程序测试和项目发布等一系列操作。更多内容参考:\u003c/p\u003e\n\u003cp\u003e如果你对CI/CD不了解的话,建议先找些文档看看。\u003c/p\u003e\n\u003cp\u003e项目源文件见\u003c/p\u003e\n\u003ch2 id=\"github-actions-术语\"\u003eGitHub Actions 术语\u003c/h2\u003e\n\u003cp\u003eGitHub Actions 相关的术语。\u003c/p\u003e\n\u003cp\u003e(1)workflow (工作流程):持续集成一次运行的过程,就是一个 workflow。\u003c/p\u003e\n\u003cp\u003e(2)job (任务):一个 workflow 由一个或 …\u003c/p\u003e"
March 27, 2020
Golang中的限速器 time/rate
"\u003cp\u003e在高并发的系统中,限流已作为必不可少的功能,而常见的限流算法有:计数器、滑动窗口、令牌桶、漏斗(漏桶)。其中滑动窗口算法、令牌桶和漏斗算法应用最为广泛。\u003c/p\u003e\n\u003ch2 id=\"常见限流算法\"\u003e常见限流算法\u003c/h2\u003e\n\u003cp\u003e这里不再对 \u003ccode\u003e计数器算法\u003c/code\u003e 和 \u003ccode\u003e滑动窗口\u003c/code\u003e 算法一一介绍,有兴趣的同学可以参考其它相关文章。\u003c/p\u003e\n\u003ch3 id=\"漏斗算法\"\u003e漏斗算法\u003c/h3\u003e\n\u003cp\u003e漏斗算法很容易理解,它就像有一个漏斗容器一样,漏斗上面一直往容器里倒水(请求),漏斗下方以\u003cstrong\u003e固定速率\u003c/strong\u003e一直流出(消费)。如果漏斗容器满的情况下,再倒入的水就会溢出,此时表示新的请求将被丢弃。可以看到这种算法在应对大的突发流量时,会造成部分请求弃用丢失。\u003c/p\u003e\n\u003cp\u003e可以看出漏斗算法能强行限制数据的传输速率。\u003cimg src=\"https://blog.haohtml.com/wp-content/uploads/2020/03/rate-limit.png\" alt=\"\"\u003e漏斗算法\u003c/p\u003e\n\u003ch3 id=\"令牌桶算法\"\u003e令牌桶算法\u003c/h3\u003e\n\u003cp\u003e从某种意义上来说,令牌算法是对漏斗算法的一种改进。对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发情况。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。\u003c/p\u003e\n\u003cp\u003e令牌桶算法是指一个固定大小的桶,可以存放的令牌的最大个数也是固定的。此算法以一种\u003cstrong\u003e固定速率\u003c/strong\u003e不断的往桶中存放令牌,而每次请求调用前必须先从桶中获取令牌才可以。否则进行拒绝或等待,直到获取到有效令牌为止。如果桶内的令牌数量已达到桶的最 …\u003c/p\u003e"
March 19, 2020
Golang中的两个定时器 ticker 和 timer
"\u003cp\u003eGolang中time包有两个定时器,分别为 \u003ccode\u003eticker\u003c/code\u003e 和 \u003ccode\u003etimer\u003c/code\u003e。两者都可以实现定时功能,但各自都有自己的使用场景。\u003c/p\u003e\n\u003ch2 id=\"ticker定时器\"\u003eTicker定时器\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;fmt\u0026#34;\n\t\u0026#34;time\u0026#34;\n)\n\nfunc main() {\n // Ticker 包含一个通道字段C,每隔时间段 d 就向该通道发送当时系统时间。\n // 它会调整时间间隔或者丢弃 tick 信息以适应反应慢的接收者。\n // 如果d \u0026lt;= 0会触发panic。关闭该 Ticker 可以释放相关资源。\n\n\tticker1 := time.NewTicker(5 * time.Second)\n\t// 一定要调用Stop(),回收资源\n\tdefer ticker1.Stop()\n\tgo func(t *time.Ticker) {\n\t\tfor {\n\t\t\t// 每5秒中从chan t.C 中读取一次\n\t\t\t\u0026lt;-t.C\n\t\t\tfmt.Println(\u0026#34;Ticker:\u0026#34;, …\u003c/code\u003e\u003c/pre\u003e"
January 18, 2020
Golang中的变量逃逸问题 变量去哪了?
"\u003cp\u003e参考阅读 \u003ca href=\"https://mp.weixin.qq.com/s/ashgWyb-w4fT47xX60yNFA\"\u003ehttps://mp.weixin.qq.com/s/ashgWyb-w4fT47xX60yNFA\u003c/a\u003e\u003c/p\u003e"
January 18, 2020
Golang中关于defer语句理解的一道题
"\u003ch2 id=\"示例\"\u003e示例\u003c/h2\u003e\n\u003cp\u003e我们先看一下源代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport \u0026#34;fmt\u0026#34;\n\nfunc f(n int) (r int) {\n\tdefer func() {\n\t\tr += n\n\t\trecover()\n\t}()\n\n\tvar fc func()\n\tdefer fc()\n\tfc = func() {\n\t\tr += 2\n\t}\n\n\treturn n + 1\n}\n\nfunc main() {\n\tfmt.Println(f(3))\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e大家感觉着打印的值是多少呢?5、9还是7?执行完以后发现是7。好像与多数理解的有些出入,为什么是7,而不是9呢。下面我们来分析一下。\u003c/p\u003e\n\u003ch2 id=\"问题分析\"\u003e问题分析\u003c/h2\u003e\n\u003cp\u003e对于defer执行的顺序是FIFO这一点都很清楚,我们只需要看搞懂f()函数的执行顺序就行了。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e执行顺序为:\u003c/strong\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e注册第1个defer 函数, 这里为匿名函数,函数体为 “func() { r += n recover() }()”,内部对应一个函数指针。这里延时函数所有相关的操作一步完成。\u003c/li\u003e\n\u003cli\u003e注册第2个defer函数,函数名为fc(),无函数体, 函数指针为\u003cstrong\u003enil\u003c/strong\u003e(也有可能指针不会空,但指针指向的内容非函数体 …\u003c/li\u003e\u003c/ol\u003e"
January 14, 2020
golang中有关select的几个知识点
"\u003cp\u003egolang中的select语句格式如下\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eselect {\n case \u0026lt;-ch1:\n // 如果从 ch1 信道成功接收数据,则执行该分支代码\n case ch2 \u0026lt;- 1:\n // 如果成功向 ch2 信道成功发送数据,则执行该分支代码\n default:\n // 如果上面都没有成功,则进入 default 分支处理流程\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e可以看到select的语法结构有点类似于switch,但又有些不同。\u003c/p\u003e\n\u003cp\u003eselect里的case后面并不带判断条件,而是一个信道的操作,不同于switch里的case,对于从其它语言转过来的开发者来说有些需要特别注意的地方。\u003c/p\u003e\n\u003cp\u003egolang 的 select 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作每个case语句里必须是一个IO操作,确切的说,应该是一个面向channel的IO操作。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e注:Go 语言的 \u003ccode\u003eselect\u003c/code\u003e 语句借鉴自 Unix 的 \u003ccode\u003eselect()\u003c/code\u003e 函数,在 Unix 中,可以通过调用 \u003ccode\u003eselect()\u003c/code\u003e 函数来监控一系列的文件句柄, …\u003c/p\u003e\u003c/blockquote\u003e"
January 11, 2020
golang性能调优工具
"\u003cp\u003e\u003ca href=\"https://cloud.tencent.com/developer/article/1478198\"\u003eGODEBUG\u003c/a\u003e, 输出结果以gc 开头的表示进行了gc垃圾回收操作,后面的数字表示gc 的次数\u003cimg src=\"https://blog.haohtml.com/wp-content/uploads/2020/01/golang_gc.jpg\" alt=\"\"\u003e \u003ca href=\"https://www.jianshu.com/p/ba9f07a346d5\"\u003epprof\u003c/a\u003e\u003c/p\u003e"
January 11, 2020
golang中的sync.Pool对象缓存
"\u003ch2 id=\"参考文章\"\u003e参考文章\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://juejin.im/post/5b7678f451882533110e8948\"\u003eGolang 的 协程调度机制 与 GOMAXPROCS 性能调优\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.cnblogs.com/sunsky303/p/9706210.html\"\u003e深入Golang之sync.Pool详解\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.jianshu.com/p/494cda4db297\"\u003egolang sync.Pool 分析\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://juejin.im/post/5d006254e51d45776031afe3\"\u003e[译] Go: 理解 Sync.Pool 的设计\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e视频 \u003ca href=\"https://time.geekbang.org/course/detail/160-87731\"\u003esync.pool对象缓存\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"知识点\"\u003e知识点\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003ePool只是一个缓存,一个缓存,一个缓存。由于生命周期受GC的影响,一定不要用于数据库连接池这类的应用场景,它只是一个缓存。\u003c/li\u003e\n\u003cli\u003egolang1.13版本对 \u003ca href=\"https://golang.org/pkg/sync/#Pool\"\u003ePool\u003c/a\u003e 进行了优化,结构体添加了两个字段 victim 和 victimSize。\u003c/li\u003e\n\u003cli\u003e适应于通过复用,降低复杂对象的创建和GC代价的场景\u003c/li\u003e\n\u003cli\u003e因为init()的时候会注册一个PoolCleanup函数,他会在gc时清除掉sync.Pool中的所有的缓存的对象。所以每个sync.Pool的生命周期为两次GC中间时段才有效,可以手动进行gc操作 \u003cstrong\u003eruntime.GC()\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e由于要保证协程安全,所以会有锁的开销\u003c/li\u003e\n\u003cli\u003e每个Pool都有一个私有池(协程安全)和共享池(\u003cstrong\u003e协程不安全\u003c/strong\u003e),其中私有池只有存放一个值。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col\u003e\n\u003cli\u003e每次Get()时会先从当前P的私有池private中获取( \u003ca href=\"https://studygolang.com/articles/11825\"\u003e类似MPG模型中的G\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e如果获 …\u003c/li\u003e\u003c/ol\u003e"
January 11, 2020
golang 的编程模式之“功能选项”
"\u003cp\u003e最近在用go重构iot中的一个服务时,发现库 \u003ca href=\"https://github.com/apache/rocketmq-client-go/releases/tag/v2.0.0-rc1\"\[email protected]\u003c/a\u003e 在初始化消费客户端实现时,实现的极其优雅,代码见 \u003ca href=\"https://github.com/apache/rocketmq-client-go/blob/v2.0.0-rc1/examples/consumer/simple/main.go#L32\"\u003ehttps://github.com/apache/rocketmq-client-go/blob/v2.0.0-rc1/examples/consumer/simple/main.go#L32\u003c/a\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ec, _ := rocketmq.NewPushConsumer(\n consumer.WithGroupName(\u0026#34;testGroup\u0026#34;),\n consumer.WithNameServer([]string{\u0026#34;127.0.0.1:9876\u0026#34;}),\n)\nerr := c.Subscribe(\u0026#34;test\u0026#34;, consumer.MessageSelector{}, func(ctx context.Context,\n msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {\n for i := …\u003c/code\u003e\u003c/pre\u003e"
November 8, 2019
Golang中的goroutine泄漏问题
"\u003cp\u003egoroutine作为Go中开发语言中的一大利器,在高并发中发挥着无法忽略的作用。但东西虽好,真正做到用好还是有一些要注意的地方,特别是对于刚刚接触这门开发语言的新手来说,稍有不慎,就极有可能导致goroutine 泄漏。\u003c/p\u003e\n\u003ch2 id=\"什么是goroutine-leak\"\u003e什么是goroutine Leak\u003c/h2\u003e\n\u003cp\u003egoroutine leak 的意思是go协程泄漏,那么什么又是协程泄漏呢?我们知道每次使用go关键字开启一个gorountine任务,经过一段时间的运行,最终是会结束,从而进行系统资源的释放回收。而如果由于操作不当导致一些goroutine一直处于阻塞状态或者永远运行中,永远也不会结束,这就必定会一直占用系统资源。最球的情况下是随着系统运行,一直在创建此类goroutine,那么最终结果就是程序崩溃或者系统崩溃。这种情况我们一般称为goroutine leak。\u003c/p\u003e\n\u003ch2 id=\"出现的问题\"\u003e出现的问题\u003c/h2\u003e\n\u003cp\u003e先看一段代码:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n \u0026#34;fmt\u0026#34;\n \u0026#34;math/rand\u0026#34;\n \u0026#34;runtime\u0026#34;\n \u0026#34;time\u0026#34;\n)\n\nfunc query() …\u003c/code\u003e\u003c/pre\u003e"
July 11, 2019
golang内存对齐(进阶必看)
"\u003cp\u003e先看一个结构体\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// 写法一\ntype T1 struct {\n\ta int8\n\tb int64\n\tc int16\n}\n\n// 写法二\ntype T2 struct {\n\ta int8\n\tc int16\n\tb int64\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e对于这两个结构体,都有a、b、c三个定义完全一样的字段,只是在定义结构体的时候字段顺序不一样而已,那么两种写法有什么影响吗?\u003c/p\u003e\n\u003cp\u003e对于新手来说,感觉着没有什么区别的,只是一个书写顺序不同而已,但对于go编译器来说,则有着很大的区别,特别是在不同架构上(32位/64位)的编译器,在一定程度上对内存的使用大小和执行效率有着一定的不同。这里的主要知识点就是golang语言中的内存对齐概念(alignment guarantee),\u003c/p\u003e\n\u003ch2 id=\"类型的尺寸和结构体字节填充structure-padding\"\u003e类型的尺寸和结构体字节填充(structure padding)\u003c/h2\u003e\n\u003cp\u003eGo白皮书只对以下种类的类型的尺寸进行了\u003ca href=\"https://golang.google.cn/ref/spec#Size_and_alignment_guarantees\"\u003e明确规定\u003c/a\u003e。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e类型种类 尺寸(字节数)\n------ ------\nbyte, uint8, int8 1\nuint16, int16 2 …\u003c/code\u003e\u003c/pre\u003e"
March 23, 2019
goroutine和线程区别
"\u003cp\u003e从调度上看,goroutine的调度开销远远小于线程调度开销。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOS的线程由OS内核调度,每隔几毫秒,一个硬件时钟中断发到CPU,CPU调用一个调度器内核函数\u003c/strong\u003e。这个函数暂停当前正在运行的线程,把他的寄存器信息保存到内存中(暂时保存线程状态),查看线程列表并决定接下来运行哪一个线程,再从内存中恢复线程的注册表信息,最后继续执行选中的线程。这种线程切换需要一个完整的上下文切换:即保存一个线程的状态到内存,再恢复另外一个线程的状态,最后更新调度器的数据结构。某种意义上,这种操作还是很慢的。\u003cimg src=\"https://blogstatic.haohtml.com//uploads/2023/09/go_scheduler.png\" alt=\"\"\u003eOS 线程调度器\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eGo运行的时候包涵一个自己的调度器\u003c/strong\u003e,这个调度器使用一个称为一个M:N调度技术,m个goroutine到n个os线程(可以用GOMAXPROCS来控制n的数量),\u003cstrong\u003eGo的调度器不是由硬件时钟来定期触发的,而是由特定的go语言结构来触发的\u003c/strong\u003e,他不需要切换到内核语境,所以调度一个goroutine比调度一个线程的成本低很多。\u003c/p\u003e\n\u003cp\u003e从栈空间上,goroutine的栈空间更加动态灵活。\u003c/p\u003e\n\u003cp\u003e每个OS的线程都有一个固定大小的栈内存,通常是2MB,栈内存用于保存在其他函数调用期间哪些正在执行或者临时暂停的函数的局部 …\u003c/p\u003e"
October 20, 2018
Golang中struct结构体的的值方法和指针方法
"\u003cp\u003e推荐:\u003ca href=\"https://mp.weixin.qq.com/s/msXzSfrDAHNPFjMtJ_i0cw\"\u003eGo的方法集详解(360云计算)\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e平时我们在写struct的时候,经常会用到一些方法,有些方法是我们熟悉的普通方法,在golang中我们称之为值方法,而另一种则是指针方法。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etype Person struct {\n Firstname string\n Lastname string\n Age uint8\n}\n// 值方法\nfunc (p Person) show() {\n fmt.Println(p.Firstname)\n}\n// 指针方法\nfunc (p *Person) show2() {\n fmt.Println(p.Firstname)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e可以看到所谓的值方法与指针方法在编写的时候,只是有无*****号的区别,这个*就是指针的意思。\u003c/p\u003e\n\u003cp\u003e那么用法又有何不同呢?\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// 值方法\nfunc (p Person) setFirstName(name string) {\n p.Firstname = name\n}\n// 指针方法\nfunc (p *Person) setFirstName2(name string) { …\u003c/code\u003e\u003c/pre\u003e"
October 19, 2018
Golang中的unsafe.Sizeof()简述
"\u003cp\u003e测试环境:\n系统 win7 64位\ngo version: go1.10 windows/amd64\u003c/p\u003e\n\u003cp\u003e我们先看一下代码的输出\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport \u0026#34;unsafe\u0026#34;\n\nfunc main() {\n\t// string\n\tstr1 := \u0026#34;abc\u0026#34;\n\tprintln(\u0026#34;string1:\u0026#34;, unsafe.Sizeof(str1)) // 16\n\tstr2 := \u0026#34;abcdef\u0026#34;\n\tprintln(\u0026#34;string2:\u0026#34;, unsafe.Sizeof(str2)) // 16\n\n\t// 数组\n\tarr1 := [...]int{1, 2, 3, 4}\n\tprintln(\u0026#34;array1:\u0026#34;, unsafe.Sizeof(arr1)) // 32 = 8 * 4\n\n\tarr2 := [...]int{1, 2, 3, 4, 5}\n\tprintln(\u0026#34;array2:\u0026#34;, unsafe.Sizeof(arr2)) // 40 = 8 * 5\n\n\t// slice 好多人 …\u003c/code\u003e\u003c/pre\u003e"
October 9, 2018
Golang中的调度器
"\u003cp\u003e\u003cimg src=\"https://blog--static.oss-cn-shanghai.aliyuncs.com//uploads/2023/09/go-mpg-1.jpg\" alt=\"\"\u003e\u003c/p\u003e\n\u003cp\u003egolang实现的协程调度器,其实就是在维护一个G、P、M三者间关系的队列。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e介绍(Introduction)\u003c/strong\u003e\n———————\nGo 1.1最大的特色之一就是这个新的调度器,由Dmitry Vyukov贡献。新调度器让并行的Go程序获得了一个动态的性能增长,针对它我不能再做点更好的工作了,我觉得我还是为它写点什么吧。\u003c/p\u003e\n\u003cp\u003e这篇博客里面大多数东西都已经被包含在了[原始设计文档]( \u003ca href=\"https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw\"\u003ehttps://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw\u003c/a\u003e)中了,这个文档的内容相当广泛,但是过于技术化了。\u003c/p\u003e\n\u003cp\u003e关于新调度器,你所需要知道的都在那个设计文档中,但是我这篇博客有图片,所以更加清晰易懂。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e带调度器的Go runtime需要什么?(What does the Go runtime need with a scheduler?)\u003c/strong\u003e\n——————————————————————————-\n但是在我们开始看新调度器之前,我们需要理解为什么需要调度器。为什么既然操作系统能为我们调度线程了,我们又创造了一个\u003cstrong\u003e用户空间调 …\u003c/strong\u003e\u003c/p\u003e"
August 31, 2018
Go的内存模型
"\u003cp\u003e\u003ca href=\"https://segmentfault.com/a/1190000008230146\"\u003ehttps://segmentfault.com/a/1190000008230146\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e原文: \u003ca href=\"https://golang.org/ref/mem\"\u003ehttps://golang.org/ref/mem\u003c/a\u003e\u003c/p\u003e"
August 30, 2018
如何优雅地关闭Go channel
"\u003cp\u003e\u003ca href=\"https://www.jianshu.com/p/d24dfbb33781\"\u003ehttps://www.jianshu.com/p/d24dfbb33781\u003c/a\u003e\u003c/p\u003e"
August 30, 2018
理解Go语言的nil
"\u003cp\u003e\u003ca href=\"https://www.jianshu.com/p/dd80f6be7969\"\u003ehttps://www.jianshu.com/p/dd80f6be7969\u003c/a\u003e\u003c/p\u003e"
August 28, 2018
[译]Go里面的unsafe包详解
"\u003cp\u003eunsafe包位置: \u003ccode\u003esrc/unsafe/unsafe.go\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e指针类型:\u003c/strong\u003e\n***类型:**普通指针,用于传递对象地址,不能进行指针运算。\n**unsafe.Pointer:**通用指针,用于转换不同类型的指针,不能进行指针运算。\n**uintptr:**用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被 GC 回收。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eunsafe.Pointer 可以和 普通指针 进行相互转换。\nunsafe.Pointer 可以和 uintptr 进行相互转换。\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e也就是说 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e一般使用流程:\u003c/strong\u003e\n第一步:将结构体 -\u0026gt; 通用指针unsafe.Pointer(struct) -\u0026gt; uintptr(通用指针)获取内存段的起始位置start_pos,并记录下来,第二步使用。\n第二步:使用start_pos + unsafe.Offsetof(s.b) -\u0026gt; 将地址转为能用指 …\u003c/p\u003e"
August 28, 2018
golang中slice切片理解总结
"\u003cp\u003e首先我们对切片有一个大概的理解,先看一下slice的内部结构,共分三部分,一个是指向底层数组的时候,一个是长度len,另一个就是slice的容量cap了。如cap不足以放在新值的时候,会产生新的内存地址申请。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://blog--static.oss-cn-shanghai.aliyuncs.com//uploads/2023/09/image-20230904182516517.png\" alt=\"image-20230904182516517\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://blog--static.oss-cn-shanghai.aliyuncs.com//uploads/2023/09/image-20230904182527333.png\" alt=\"image-20230904182527333\"\u003e\u003c/p\u003e\n\u003cp\u003e先看代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport \u0026#34;fmt\u0026#34;\n\nfunc main() {\n\n // 创建一个切片,长度为9,容量为10\n fmt.Println(\u0026#34;----- 1.测试切片变量append的影响(未申请新的内存空间)-----\u0026#34;)\n a := make([]int, 9,10)\n fmt.Printf( \u0026#34;%p len=%d cap=%d %vn\u0026#34; , a, len(a), cap(a), a)\n\n // 切片进行append操作,由于原来len(a)长度为9,而cap(a)容量为10,未达到扩展内存的要求,此时新创建的切片变量还指向原来的底层数组,只是数组的后面添加一个新值\n // 此时一共两个切片变量,一个是a,另一个是s4。但共指向的一个内存地址\n s4 := …\u003c/code\u003e\u003c/pre\u003e"
July 2, 2018
Go中复制文件的3种方式
"\u003cp\u003e\u003ca href=\"https://opensource.com/article/18/6/copying-files-go\"\u003ehttps://opensource.com/article/18/6/copying-files-go\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e更多: \u003ca href=\"https://opensource.com/tags/go\"\u003ehttps://opensource.com/tags/go\u003c/a\u003e\u003c/p\u003e"
June 11, 2018
Linux下对进程通信管理的信号机制概述
"\u003cp\u003e今天看到了篇使用golang实现的系统无感重启的文章, \u003ca href=\"https://gravitational.com/blog/golang-ssh-bastion-graceful-restarts/\"\u003ehttps://gravitational.com/blog/golang-ssh-bastion-graceful-restarts/\u003c/a\u003e,一般用来平滑处理一些系统服务,避免先停止再启用导致的服务不可用的情况。其中用到了信号机制,这里找了一些文章主要有来介绍这方面的文章,以便加深理解。 \u003ca href=\"https://blog.csdn.net/junyucsdn/article/details/50519248\"\u003ehttps://blog.csdn.net/junyucsdn/article/details/50519248\u003c/a\u003e \u003ca href=\"https://blog.csdn.net/tiany524/article/details/17048069\"\u003ehttps://blog.csdn.net/tiany524/article/details/17048069\u003c/a\u003e \u003ca href=\"https://my.oschina.net/chenliang165/blog/125825\"\u003ehttps://my.oschina.net/chenliang165/blog/125825\u003c/a\u003e\u003c/p\u003e"
March 8, 2018
Go中slice作为参数传递的一些“坑”
"\u003cp\u003e看明白了这篇文章,下面的例子基本也就明白了\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport \u0026#34;fmt\u0026#34;\n\nfunc main() {\n\ta := []int{1,2,3}\n\tabc(a)\n\tfmt.Println(a)\n}\nfunc abc(a []int) {\n\ta[0] = 2 //修改后还是原来的a\n\ta = append(a, 4) // 此a非原a,使用append导致了重新分配内存地址(存储空间不足,系统自动分配一块新的足够大的内存地址,此时a的物理内存地址已经发行了变化,并将原来a的值copy一份到新的内存地址,所以这里修改的只是新内存地址的值,原来内存地址的值并没有改变),试着删除这行运行一次再看结果\n\tfmt.Println(a)\n\n\ta[0] = 7 // 新a,因为上面执行了append\n\tfmt.Println(a)\n\n\tfmt.Printf(\u0026#34;\\n===\\n\u0026#34;)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e解释:\n[\u003cimg src=\"https://blog--static.oss-cn-shanghai.aliyuncs.com//uploads/2023/09/golang_slice_append.png\" alt=\"\"\u003e][1]\u003c/p\u003e"
March 3, 2018
golang中string rune byte 三者的关系
"\u003cp\u003e\u003ccode\u003eGo\u003c/code\u003e 语言中 \u003ccode\u003ebyte\u003c/code\u003e 和 \u003ccode\u003erune\u003c/code\u003e 实质上就是 \u003ccode\u003euint8\u003c/code\u003e 和 \u003ccode\u003eint32\u003c/code\u003e 类型。 \u003ccode\u003ebyte\u003c/code\u003e 用来强调数据是 \u003ccode\u003eraw data\u003c/code\u003e,而不是数字;而 \u003ccode\u003erune\u003c/code\u003e 用来表示 \u003ccode\u003eUnicode\u003c/code\u003e 的 \u003ccode\u003ecode point\u003c/code\u003e。参考 \u003ca href=\"https://golang.org/ref/spec#Numeric_types\"\u003e规范.\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e在Golang中 string 底层是用byte字节数组存储的,并且是不可以修改的。\u003c/p\u003e\n\u003ch1 class=\"postTitle.wp-block-heading\" id=\"go语言中的byte和rune区别对比\"\u003eGo语言中的byte和rune区别、对比\u003c/h1\u003e\n\u003cp\u003e例如\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003es:=\u0026#34;Go编程\u0026#34;\nfmt.Println(len(s)) //输出结果应该是8因为中文字符是用3个字节存的(2+3*2=8)。\nfmt.Printf(\u0026#34;%d\u0026#34;, len(string(rune(\u0026#39;编\u0026#39;)))) //经测试一个汉字确实占用3个字节,所以结果是3\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e如果想要获得字符个数的话,需要先转换为rune切片再使用内置的len函数\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efmt.Println(len([]rune(s))) // 结果就是4了。\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e所以用string存储unicode的话,如果有中文,按下标是访问不到的,因为你只能得到一个byte。 要想访问中文的话,还是要用rune切片,这样就能按下表访问。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e总结:\u003c/strong\u003e\n\u003ccode\u003erune …\u003c/code\u003e\u003c/p\u003e"
April 3, 2016
[翻译]理解 GO 语言的内存使用
"\u003cp\u003e许多人在刚开始接触 Go 语言时,经常会有的疑惑就是“为什么一个 Hello world 会占用如此之多的内存?”。 \u003ca href=\"https://deferpanic.com/blog/understanding-golang-memory-usage/\" title=\"Understanding Go Lang Memory Usage\"\u003eUnderstanding Go Lang Memory Usage\u003c/a\u003e 很好的解释了这个问题。不过“简介”就是“简介”,更加深入的内容恐怕要读者自己去探索了。另外,文章写到最后,作者飘了,估计引起了一些公愤,于是又自己给自己补刀,左一刀,右一刀……\u003c/p\u003e\n\u003cp\u003e————翻译分隔线————\u003c/p\u003e\n\u003ch1 id=\"理解-go-语言的内存使用\"\u003e理解 Go 语言的内存使用\u003c/h1\u003e\n\u003cp\u003e\u003cem\u003e2014年12月22日,星期一\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e温馨提示\u003c/strong\u003e:这仅是关于 Go 语言内存的简介,俗话说不入虎穴、焉得虎子,读者可以进行更加深入的探索。\u003c/p\u003e\n\u003cp\u003e大多数 Go 开发者都会尝试像这样简单的 hello world 程序:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\u0026#34;fmt\u0026#34;\n\u0026#34;time\u0026#34;\n)\n\nfunc main() {\nfmt.Println(\u0026#34;hi\u0026#34;)\n\ntime.Sleep(30 * time.Second)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e然后他们就完全崩溃了。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://blog.haohtml.com/wp-content/uploads/2016/04/high_mem-300x38.png\"\u003e\u003cimg src=\"http://blog.haohtml.com/wp-content/uploads/2016/04/high_mem-300x38.png\" alt=\"high_mem-300x38\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e这个笔记本也只有 16 G 内存!\u003c/p\u003e\n\u003ch2 id=\"虚拟内存-vs-常驻内存\"\u003e虚拟内存 vs 常驻内存\u003c/h2\u003e\n\u003cp\u003eGo 管理内存的方式可能与你以 …\u003c/p\u003e"
March 25, 2016
Profiling Go Programs
"\u003cp\u003e转自: \u003ca href=\"http://blog.golang.org/profiling-go-programs\"\u003ehttp://blog.golang.org/profiling-go-programs\u003c/a\u003e (需翻墙)\u003c/p\u003e\n\u003ch1 id=\"the-go-blog\"\u003e\u003ca href=\"http://blog.golang.org/\"\u003eThe Go Blog\u003c/a\u003e\u003c/h1\u003e\n\u003ch3 class=\"title\" id=\"profiling-go-programs\"\u003e\u003ca href=\"http://blog.golang.org/profiling-go-programs\"\u003eProfiling Go Programs\u003c/a\u003e\u003c/h3\u003e\n\u003cp\u003e24 June 2011\u003c/p\u003e\n\u003cp\u003eAt Scala Days 2011, Robert Hundt presented a paper titled \u003ca href=\"http://research.google.com/pubs/pub37122.html\"\u003eLoop Recognition in C++/Java/Go/Scala.\u003c/a\u003e The paper implemented a specific loop finding algorithm, such as you might use in a flow analysis pass of a compiler, in C++, Go, Java, Scala, and then used those programs to draw conclusions about typical performance concerns in these languages. The Go program presented in that paper runs quite …\u003c/p\u003e"
March 24, 2016
golang中并发实例
"\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;fmt\u0026#34;\n\t//\u0026#34;runtime\u0026#34;\n\t\u0026#34;os\u0026#34;\n\t\u0026#34;runtime/pprof\u0026#34; // 引用pprof package\n\t\u0026#34;time\u0026#34;\n)\n\nfunc main() {\n\tf, _ := os.Create(\u0026#34;profile_file\u0026#34;)\n\tpprof.StartCPUProfile(f) // 开始cpu profile,结果写到文件f中\n\tdefer pprof.StopCPUProfile() // 结束profile\n\n\tstartTime := time.Now().Second()\n\t//runtime.GOMAXPROCS(runtime.NumCPU())\n\n\t// 注意这里的缓存大小\n\tch := make(chan int, 100)\n\tquit := make(chan bool)\n\n\t// 注意这里把读取chan操作放在了写入chan之前了(为了安全建议对chan的goroutines读取放在前面,写入放在后 …\u003c/code\u003e\u003c/pre\u003e"
March 24, 2016
在Golang中使用json
"\u003cblockquote\u003e\n\u003cp\u003e由于要开发一个小型的web应用,而web应用大部分都会使用json作为数据传输的格式,所以有了这篇文章。\u003c/p\u003e\u003c/blockquote\u003e\n\u003ch4 id=\"包引用\"\u003e包引用\u003c/h4\u003e\n\u003cp\u003eimport (\n\u0026ldquo;encoding/json\u0026rdquo;\n\u0026ldquo;github.com/bitly/go-simplejson\u0026rdquo; // for json get\n)\u003c/p\u003e\n\u003ch4 id=\"用于存放数据的结构体\"\u003e用于存放数据的结构体\u003c/h4\u003e\n\u003cp\u003etype MyData struct {\nName string \u003ccode\u003ejson:\u0026quot;item\u0026quot;\u003c/code\u003e\nOther float32 \u003ccode\u003ejson:\u0026quot;amount\u0026quot;\u003c/code\u003e\n}\u003c/p\u003e\n\u003cp\u003e这里需要注意的就是后面单引号中的内容。\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003ejson:\u0026quot;item\u0026quot;\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e这个的作用,就是Name字段在从结构体实例编码到JSON数据格式的时候,使用item作为名字。算是一种重命名的方式吧。\u003c/p\u003e\n\u003ch4 id=\"编码json\"\u003e编码JSON\u003c/h4\u003e\n\u003cp\u003evar detail MyData\u003c/p\u003e\n\u003cp\u003edetail.Name = \u0026ldquo;1\u0026rdquo;\ndetail.Other = \u0026ldquo;2\u0026rdquo;\u003c/p\u003e\n\u003cp\u003ebody, err := json.Marshal(detail)\nif err != nil …\u003c/p\u003e"
March 24, 2016
golang的json操作
"\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n \u0026#34;encoding/json\u0026#34;\n \u0026#34;fmt\u0026#34;\n \u0026#34;os\u0026#34;\n)\n\ntype ConfigStruct struct {\n Host string `json:\u0026#34;host\u0026#34;`\n Port int `json:\u0026#34;port\u0026#34;`\n AnalyticsFile string `json:\u0026#34;analytics_file\u0026#34;`\n StaticFileVersion int `json:\u0026#34;static_file_version\u0026#34;`\n StaticDir string `json:\u0026#34;static_dir\u0026#34;`\n TemplatesDir string `json:\u0026#34;templates_dir\u0026#34;`\n SerTcpSocketHost …\u003c/code\u003e\u003c/pre\u003e"
March 23, 2016
golang中的md5的用法
"\u003cp\u003e代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;crypto/md5\u0026#34;\n\t\u0026#34;encoding/hex\u0026#34;\n\t\u0026#34;fmt\u0026#34;\n)\n\nfunc main() {\n\t// md5 加密的第一种方法\n\tsrcData := []byte(\u0026#34;iyannik0215\u0026#34;)\n\tcipherText1 := md5.Sum(srcData)\n\tfmt.Printf(\u0026#34;md5 encrypto is \u0026#34;iyannik0215\u0026#34;: %x n\u0026#34;, cipherText1)\n\n\t// md5 加密的第二种方法\n\thash := md5.New()\n\thash.Write(srcData)\n\tcipherText2 := hash.Sum(nil)\n\thexText := make([]byte, 32)\n\thex.Encode(hexText, cipherText2)\n\tfmt.Println(\u0026#34;md5 encrypto is \u0026#34;iyannik0215\u0026#34;:\u0026#34;, …\u003c/code\u003e\u003c/pre\u003e"
June 15, 2015
golang中chan实例
"\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport \u0026#34;fmt\u0026#34;\n\nfunc main() {\n data := make(chan int) // 数据交换队列\n exit := make(chan bool) // 退出通知\n\ngo func() {\n for d := range data { // 从队列迭代接收数据,直到 close 。\n fmt.Println(d)\n }\n\n fmt.Println(\u0026#34;recv over.\u0026#34;)\n exit \u0026lt;- true // 发出退出通知。\n}()\n\ndata \u0026lt;- 1 // 发送数据。\ndata \u0026lt;- 2\ndata \u0026lt;- 3\n\nclose(data) // 关闭队列。\n\nfmt.Println(\u0026#34;send over.\u0026#34;)\n\n\u0026lt;-exit // 等待退出通知。\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e输出结果:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e1\n2\n3\nsend over.\nrecv over.\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e而如果将上面与 exit chan有关的三行删除掉,则结果为:\u003c/p\u003e"
June 13, 2015
golang中chan的理解与使用教程
"\u003cp\u003e对于 chan 介绍见: \u003ca href=\"https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/02.7.md\"\u003ehttps://github.com/astaxie/build-web-application-with-golang/blob/master/zh/02.7.md\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e这里我们主要通过实例来介绍对chan的理解及用法.\u003c/p\u003e\n\u003ch1 id=\"无buffer的channels\"\u003e无Buffer的Channels\u003c/h1\u003e\n\u003cp\u003e实例1:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunc main() {\nci := make(chan int)\n\nci \u0026lt;- 4\n\nvalue := \u0026lt;-ci\nfmt.Println(value)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e执行结果错误为:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003efatal error: all goroutines are asleep - deadlock!\ngoroutine 1 [chan send]:\u003c/p\u003e\u003c/blockquote\u003e\n\u003cp\u003e从上面“fatal error: all goroutines are asleep - deadlock!” 这句我们可以看出是groutings 阻塞了,这里为写阻塞,从“goroutine 1 [chan send]”可以看出来。\u003c/p\u003e\n\u003cp\u003e这一点文档里已经说明阻塞的原因了:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使 …\u003c/p\u003e\u003c/blockquote\u003e"
June 9, 2015
golang中flag包的用法
"\u003cp\u003egolang中flag包主要用来CLI下,获取命令参数,示例如下mysql.go:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\u0026#34;flag\u0026#34;\n\u0026#34;fmt\u0026#34;\n)\n\nfunc main() {\n host := flag.String(\u0026#34;h\u0026#34;, \u0026#34;localhost\u0026#34;, \u0026#34;请指定一个主机\u0026#34;)\n user := flag.String(\u0026#34;u\u0026#34;, \u0026#34;root\u0026#34;, \u0026#34;请指定数据库用户\u0026#34;)\n port := flag.Int(\u0026#34;P\u0026#34;, 3306, \u0026#34;Port number to use for commection or 0 for default to, in port 3306\u0026#34;)\n\n //var name string\n //flag.StringVar(\u0026amp;name, \u0026#34;u\u0026#34;, \u0026#34;root\u0026#34;, \u0026#34;请指定用户名\u0026#34;)\n\n flag.Parse() // …\u003c/code\u003e\u003c/pre\u003e"
April 8, 2015
Golang语言的GOPATH与工作目录详解
"\u003cp\u003e这篇文章主要介绍了Go语言的GOPATH与工作目录详解,本文详细讲解了GOPATH设置、应用目录结构、编译应用等内容,需要的朋友可以参考下\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eGOPATH设置\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003ego 命令依赖一个重要的环境变量:$GOPATH\u003c/p\u003e\n\u003cp\u003e(注:这个不是Go安装目录( \u003cstrong\u003eGOROOT\u003c/strong\u003e)。下面以笔者的工作目录为说明,请替换自己机器上的工作目录。)\u003c/p\u003e\n\u003cp\u003e在类似 Unix 环境大概这样设置:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eexport GOPATH=/home/apple/mygo\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e为了方便,应该把新建以上文件夹,并且把以上一行加入到 .bashrc 或者 .zshrc 或者自己的 sh 的配置文件中。\u003c/p\u003e\n\u003cp\u003eWindows 设置如下,新建一个环境变量名称叫做GOPATH:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eGOPATH=c:mygo\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003eGOPATH允许多个目录,当有多个目录时,请注意分隔符,多个目录的时候Windows是分号,Linux系统是冒号,当有多个GOPATH时,默认会将go get的内容放在第一个目录下\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e以上 $GOPATH 目录约定有三个子目录:\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e1.src 存放源代码(比如:.go .c .h .s等)\u003c/p\u003e\n\u003cp\u003e2.pkg 编译后生成的文件(比如:.a)\u003c/p\u003e\n\u003cp\u003e3.bin 编译后生成的可执行文件( …\u003c/p\u003e"
June 20, 2014
golint—golang代码质量检测
"\u003cp\u003egithub: \u003ca href=\"https://github.com/golang/lint\"\u003ehttps://github.com/golang/lint\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003egolint是类似javascript中的jslint的工具,主要功能就是检测代码中不规范的地方。golint用于检测go代码。\u003c/p\u003e\n\u003ch2 id=\"使用\"\u003e使用\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e$ go get github.com/golang/lint\n$ go install github.com/golang/lint\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003ccode\u003egolint 文件名或者目录\u003c/code\u003e\n检测对应的代码。\u003c/p\u003e\n\u003cp\u003egolint会输出一些代码存在的问题:\n比如:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erecorder.go:55:5: exported var RecordBind should have comment or be unexported\nrecorder.go:158:1: exported function Record_ErrorRecord should have comment or be unexported\nrecorder.go:173:6: don\u0026#39;t use underscores in Go names; type Data_MemStats should be DataMemStats …\u003c/code\u003e\u003c/pre\u003e"
March 8, 2014
golang中for循环方法的差异
"\u003cp\u003e用for循环遍历字符串时,也有 byte 和 rune 两种方式.第一种试为byte,第二种rune。\u003c/p\u003e\n\u003cp\u003egolang中string rune byte 三者的关系 \u003ca href=\"https://blog.haohtml.com/archives/17646\"\u003ehttps://blog.haohtml.com/archives/17646\u003c/a\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n \u0026#34;fmt\u0026#34;\n)\n\nfunc main() {\n s := \u0026#34;abc汉字\u0026#34;\n\n for i := 0; i \u0026lt; len(s); i++ {\n fmt.Printf(\u0026#34;%c,\u0026#34;, s[i])\n }\n\n fmt.Println()\n\n for _, r := range s {\n fmt.Printf(\u0026#34;%cn\u0026#34;, r)\n }\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e输出结果:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ea,b,c,æ,±,,å,,,\na\nb\nc\n汉\n字\n\u003c/code\u003e\u003c/pre\u003e"
October 31, 2013
Golang import使用说明
"\u003cp\u003e我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\nimport(\n\u0026#34;fmt\u0026#34;\n)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e然后我们代码里面可以通过如下的方式调用\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efmt.Println(\u0026#34;hello world\u0026#34;)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e上面这个fmt是Go语言的标准库,他其实是去goroot下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:\u003c/p\u003e\n\u003cp\u003e1.相对路径\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eimport “./model” //当前文件同一目录的model目录,但是不建议这种方式来import\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e2.绝对路径\u003c/p\u003e\n\u003cp\u003eimport “shorturl/model” //加载gopath/src/shorturl/model模块\u003c/p\u003e\n\u003cp\u003e上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e1.点操作\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e我们有时候会看到如下的方式导入包\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eimport(\n. \u0026#34;fmt\u0026#34;\n)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用 …\u003c/p\u003e"
October 22, 2013
golang实现验证电子信箱和手机格式合法性(正则表达式)
"\u003cp\u003e邮箱验证\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunc checkEmail(email string) (b bool) {\n if m, _ := regexp.MatchString(\u0026#34;^([a-zA-Z0-9\\_-])+@([a-zA-Z0-9\\_-])+(.[a-zA-Z0-9_-])+\u0026#34;, email); !m {\n \treturn false\n }\n return true\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e手机验证\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\nimport (\n\u0026#34;regexp\u0026#34;\n)\n\nconst (\n\tregular = \u0026#34;^(13[0-9]|14[57]|15[0-35-9]|18[07-9])\\\\d{8}$\u0026#34;\n)\n\nfunc validate(mobileNum string) bool {\n reg := regexp.MustCompile(regular)\n return reg.MatchString(mobileNum)\n}\n\nfunc main() {\n if validate(\u0026#34;13888888888\u0026#34;) { …\u003c/code\u003e\u003c/pre\u003e"
October 22, 2013
[必读]golang语言报错信息fatal error: all goroutines are asleep – deadlock!
"\u003cp\u003e出错信息\u003cstrong\u003efatal error: all goroutines are asleep – deadlock!\u003c/strong\u003e\n\u003cstrong\u003e出错信息的意思是:\u003c/strong\u003e\n在main goroutine线,期望从管道中获得一个数据,\u003cstrong\u003e而这个数据必须是其他goroutine线放入管道的\u003c/strong\u003e\n但是其他goroutine线都已经执行完了(all goroutines are asleep),那么就永远不会有数据放入管道。\n所以,main goroutine线在等一个永远不会来的数据,那整个程序就永远等下去了。\n这显然是没有结果的,所以这个程序就说“算了吧,不坚持了,我自己自杀掉,报一个错给代码作者,我被deadlock了”\n例子:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nfunc main() {\n c := make(chan bool)\n go func() {\n c \u0026lt;- true\n }()\n \u0026lt;-c //这里从c管道,取到一个true\n \u0026lt;-c //这行导致deadlock,因为这时的c管道,永远都取不到数据(注释掉这行就不报错)\n}\n\u003c/code\u003e\u003c/pre\u003e"
October 22, 2013
golang日志模块测试
"\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n \u0026#34;fmt\u0026#34;\n \u0026#34;log\u0026#34;\n \u0026#34;os\u0026#34;\n)\n\nfunc main(){\n logfile,err := os.OpenFile(\u0026#34;d:/workspace/golang/test.log\u0026#34;,os.O\\_RDWR|os.O\\_CREATE|os.O_APPEND,0);\n if err!=nil {\n fmt.Printf(\u0026#34;%s\\r\\n\u0026#34;,err.Error());\n os.Exit(-1);\n }\n defer logfile.Close();\n \n logger := log.New(logfile,\u0026#34;\\r\\n\u0026#34;,log.Ldate|log.Ltime|log.Llongfile);\n logger.Println(\u0026#34;hello\u0026#34;);\n logger.Println(\u0026#34;oh….\u0026#34;);\n logger.Fatal(\u0026#34;test\u0026#34;); …\u003c/code\u003e\u003c/pre\u003e"
October 7, 2013
[翻译]绝妙的 channel
"\u003cp\u003e在编写 golang 程序的过程中,channel 会经常使用。本文对 channel 的使用的确很特别,同时也非常实用。\u003c/p\u003e\n\u003cp\u003e原文在此: \u003ca href=\"http://dave.cheney.net/2013/04/30/curious-channels\" title=\"Curious channels\"\u003ehttp://dave.cheney.net/2013/04/30/curious-channels\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e在编写 golang 程序的过程中,channel 会经常使用。本文对 channel 的使用的确很特别,同时也非常实用。\u003c/p\u003e\n\u003cp\u003e原文在此:http://dave.cheney.net/2013/04/30/curious-channels\u003c/p\u003e\n\u003cp\u003e翻译:\u003ca href=\"http://mikespook.com/2013/05/%E7%BF%BB%E8%AF%91%E7%BB%9D%E5%A6%99%E7%9A%84-channel/#more-1635\"\u003ehttp://mikespook.com/2013/05/%E7%BF%BB%E8%AF%91%E7%BB%9D%E5%A6%99%E7%9A%84-channel/#more-1635\u003c/a\u003e\u003c/p\u003e"
October 4, 2013
VIM编辑器下go语法高亮显示
"\u003ch1 id=\"go-in-vim\"\u003eGo in Vim\u003c/h1\u003e\n\u003cp\u003eThe standard Go distribution includes a Go syntax file for Vim in \u003ccode\u003ego/misc/vim/\u003c/code\u003e.\u003c/p\u003e\n\u003ch2 id=\"installation-instructions\"\u003eInstallation Instructions\u003c/h2\u003e\n\u003cp\u003ePlace \u003ccode\u003e$GOROOT/misc/vim/syntax/go.vim\u003c/code\u003e in \u003ccode\u003e~/.vim/syntax/\u003c/code\u003e and put the following in \u003ccode\u003e~/.vim/ftdetect/go.vim\u003c/code\u003e:\u003c/p\u003e\n\u003cp\u003e在go的安装目录里有/misc/vim/syntax 他 /misc/vim/ftdetect 两个目录,将里面的文件复制到~/.vim/相应的目录里即可。\u003c/p\u003e\n\u003cp\u003eau BufRead,BufNewFile *.go set filetype=go\u003c/p\u003e\n\u003ch2 id=\"extras-and-alternative-files\"\u003eExtras and Alternative Files\u003c/h2\u003e\n\u003cp\u003eAn alternative indent file for Vim by Alecs King can be found \u003ca href=\"http://go-lang.cat-v.org/text-editors/vim/go-indent.vim\"\u003ehere\u003c/a\u003e.\u003c/p\u003e\n\u003ch2 id=\"autocompletion\"\u003eAutocompletion\u003c/h2\u003e\n\u003cp\u003eThe \u003ca href=\"http://github.com/nsf/gocode\"\u003egocode\u003c/a\u003e daemon by nsf includes a vim script …\u003c/p\u003e"
October 2, 2013
gozmq的安装与使用教程(zeromq分布式消息队列+golang)
"\u003cp\u003e实现功能:用go实现消息队列的写入与读取(打算用在发送邮件服务)\u003c/p\u003e\n\u003cp\u003e环境工具:\nCentos 64X 6.4\nzeromq 3.2.4: \u003ca href=\"http://www.zeromq.org\"\u003ezeromq.org\u003c/a\u003e\ngolang: \u003ca href=\"http://golang.org/\"\u003ehttp://golang.org/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e一.安装golang( \u003ca href=\"http://golang.org/doc/install\"\u003ehttp://golang.org/doc/install\u003c/a\u003e)\u003c/strong\u003e\n这一步很简单,只需要从 \u003ca href=\"http://code.google.com/p/go/downloads\"\u003ehttp://code.google.com/p/go/downloads\u003c/a\u003e 下载到服务器,解压到/usr/local/go目录,再设置一下系统变量就可以了.\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ewget https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz\ntar -C /usr/local -xzf go1.1.2.linux-amd64.tar.gz\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003e设置系统变量GOROOT\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eAdd \u003ccode\u003e/usr/local/go/bin\u003c/code\u003e to the \u003ccode\u003ePATH\u003c/code\u003e environment variable. You can do this by adding this line to your \u003ccode\u003e/etc/profile\u003c/code\u003e (for a system-wide …\u003c/p\u003e"
August 12, 2013
golang中结构体的初始化方法的不同用法(new方法)
"\u003cp\u003e自定义一个结构体\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003etype Rect struct {\n x, y float64\n width, height float64\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e初始化方法:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003erect1 := new(Rect)\nrect2 := \u0026amp;Rect{}\nrect3 := \u0026amp;Rect{0, 0, 100, 200}\nrect4 := \u0026amp;Rect{width:100, height:200}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e注意这几个变量全部为指向Rect结构的指针(\u003cstrong\u003e指针变量\u003c/strong\u003e),因为使用了new()函数和\u0026amp;操作符。\u003c/p\u003e\n\u003cp\u003e而如果使用方法\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ea := Rect{}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e则表示这个是一个Rect{}\u003cstrong\u003e结构类型\u003c/strong\u003e.两者是不一样的.参考代码:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunc main() {\na := Rect{}\na.x = 15\n\nrect1 := \u0026amp;Rect{0, 0, 100, 200}\nrect1.x = 10\n\nfmt.Printf(\u0026#34;%v\\n%T\\n\u0026#34;, a, a)\nfmt.Printf(\u0026#34;%v\\n%T\\n\u0026#34;, rect1, rect1)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e运行结果为:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e{15 0 0 0}\n main.Rect …\u003c/code\u003e\u003c/pre\u003e"
August 11, 2013
golang中的文档管理
"\u003cp\u003efoo.go\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e// CopyRight 2013 The Go Author. All Right reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE fifle.\n\n/*\nPackage foo implements a set of simple mathematical functions. These comments are for\ndemonstration purpose only. Nothing more.\n\nIf you have any questions,please don’t hesitate to add yourself to\[email protected].\n\nyou can alse visit golang.org for full Go documentation.\n*/\n\npackage foo\n\nimport (\n\t\u0026#34;fmt\u0026#34;\n)\n\n// …\u003c/code\u003e\u003c/pre\u003e"
August 11, 2013
golang中的map数据类型操作实例
"\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;fmt\u0026#34;\n)\n\ntype stu struct {\n\tName string\n\tAge int\n}\n\nfunc main() {\n\n\t// 声明一个map变量student,键名为string,值为stu\n\tvar student map[string]stu\n\n\t// 给map变量创建值,同时指定最多可以存储5个stu值\n\tstudent = make(map[string]stu, 5)\n\n\t// map元素赋值\n\tstudent[\u0026#34;stu1\u0026#34;] = stu{\u0026#34;zhao\u0026#34;, 25}\n\tstudent[\u0026#34;stu2\u0026#34;] = stu{\u0026#34;zhang\u0026#34;, 28}\n\tstudent[\u0026#34;stu3\u0026#34;] = stu{\u0026#34;sun\u0026#34;, 32}\n\tstudent[\u0026#34;stu4\u0026#34;] = stu{\u0026#34;li\u0026#34;, 40}\n\tstudent[\u0026#34;stu5\u0026#34;] = stu{}\n\n\t//上面方式的简写方法\n\t/* …\u003c/code\u003e\u003c/pre\u003e"
August 9, 2013
go语言单元测试
"\u003cp\u003eGo本身提供了一套轻量级的测试框架.符合规则的测试代码会在运行测试时被自动识别并执行.单元测试源文件的命名规则如平衡点:在需要测试的包下面创建以”_test”结尾的go文件,开如[^.]*_test.go\u003c/p\u003e\n\u003cp\u003eGo单元测试函数分为两在类.\u003cstrong\u003e功能测试函数\u003c/strong\u003e和\u003cstrong\u003e性能测试函数\u003c/strong\u003e,分别以Test和Benchmark为函数名前缀并以*testing.T 和 *testing.B 为单一参数的函数。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunc TestAdd1(t *testing.T)\nfunc BenchmarkAdd1(t *testing.T)\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e测试工具会根据函数中的实际执行动作得到不同的测试结果。\u003c/p\u003e\n\u003cp\u003e功能测试函数会根据测试代码执行过程中是否发生错误来反馈结果;\n性能测试函数仅仅打印出来测试所花费时间,用来判断程序性能;\u003c/p\u003e\n\u003ch1 id=\"准备\"\u003e准备\u003c/h1\u003e\n\u003cp\u003e新建一个文件,命名为 go_test.go\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage go_test\n\nimport \u0026#34;testing\u0026#34;\n\nfunc Add(a, b int) int {\n return a + b\n}\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"功能测试\"\u003e功能测试\u003c/h1\u003e\n\u003cp\u003e在go_test.go文件里添加以下代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003efunc TestAdd1(t …\u003c/code\u003e\u003c/pre\u003e"
July 23, 2013
用golang发送邮件
"\u003cp\u003e配置文件 \u003ccode\u003econf.json\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e{\n\u0026#34;Username\u0026#34;: \u0026#34;[email protected]\u0026#34;,\n\u0026#34;Password\u0026#34;: \u0026#34;123456\u0026#34;,\n\u0026#34;Smtphost\u0026#34;:\u0026#34;smtp.163.com:25\u0026#34;\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e主程序 sendmail.go\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\t\u0026#34;encoding/json\u0026#34;\n\t\u0026#34;fmt\u0026#34;\n\t\u0026#34;io\u0026#34;\n\t\u0026#34;log\u0026#34;\n\t\u0026#34;net/smtp\u0026#34;\n\t\u0026#34;os\u0026#34;\n\t\u0026#34;strings\u0026#34;\n)\n\ntype cfgmail struct {\n\tUsername string\n\tPassword string\n\tSmtphost string\n}\n\ntype cfg struct {\n\tName, Text string\n}\n\nfunc main() {\n\n\t// 从json文件中读取发送邮件服务器配置信息\n\tcfgjson := getConf()\n\n\tvar …\u003c/code\u003e\u003c/pre\u003e"
June 18, 2013
测试golang中的多核多线程
"\u003cp\u003e“并发 (concurrency)” 和 “并行 ( parallelism)” 是不同的。在单个 CPU 核上,线程通过时间片或者让出控制权来实现任务切换,达到 “同时” 运行多个任务的⺫的,这就是所谓的并发。但实际上任何时刻都只有一个任务被执行,其他任务通过某种算法来排队。\u003c/p\u003e\n\u003cp\u003e多核 CPU 可以让同个进程内的 “多个线程” 做到真正意义上的同时运,它们之间不需要排队 (依然会发生排队,因为线程数量可能超出 CPU 核数量,还有其他的进程等等。这里说的是一个理想状况),这才是并行。除了多核,并行计算还可能是多台机器上部署运行。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\u0026#34;fmt\u0026#34;\n\u0026#34;runtime\u0026#34;\n)\n\nfunc test(c chan bool, n int) {\n\nx := 0\nfor i := 0; i \u0026lt; 1000000000; i++ {\nx += i\n}\n\nprintln(n, x)\n\nif n == 9 {\nc \u0026lt;- true\n}\n}\n\nfunc main() {\nruntime.GOMAXPROCS(1) //设置cpu …\u003c/code\u003e\u003c/pre\u003e"
June 17, 2013
golang中的Array 、Slices 和 Maps
"\u003cp\u003e**注意\u003ccode\u003eslice\u003c/code\u003e和数组在声明时的区别:**声明数组时,方括号内写明了数组的长度或使用\u003ccode\u003e...\u003c/code\u003e自动计算长度,而声明\u003ccode\u003eslice\u003c/code\u003e时,方括号内没有任何字符。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003earr1 := [10]int{1,2,3,4} //数组,长度为10,只有4个元素指定,其它的元素值默认为0\narr2 := [...]string{\u0026#34;a\u0026#34;,\u0026#34;b\u0026#34;,\u0026#34;c\u0026#34;} //数组,长度自适应,这里长度为3\ns1 := []int{1,2,3,4} //slice,目前长度为4,可能通过append来动态添加元素个数\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e示例:\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n \u0026#34;fmt\u0026#34;\n)\n\nfunc main() {\n\n//array example\n arr := [10]int{1, 2, 3} //array 指定前三个值,其它值使用默认类型值0\n fmt.Println(len(arr))\n fmt.Println(arr)\n //a1 := append(arr, 4, 5) //数组不支持append,只有slice才支持append …\u003c/code\u003e\u003c/pre\u003e"
June 4, 2013
golang中包的用法
"\u003cp\u003e将d:/gotest/ 目录加入到GOPATH中.这里会涉及到包和结构体还有一些方法的用法,可以再深入的了了解一下\u003c/p\u003e\n\u003cp\u003e注意一下一些struct和 func 名称的大小写问题.\u003c/p\u003e\n\u003cp\u003e首先要在 \u003cstrong\u003e$GOPATH/src\u003c/strong\u003e 目录里创建一个包名目录,这里包名目录为stu,与文件名一样(也可以不一样),大概流程参考:\n\u003ca href=\"http://blog.haohtml.com/wp-content/uploads/2013/06/GO_package.png\"\u003e\u003cimg src=\"https://blogstatic.haohtml.com//uploads/2023/09/GO_package.png\" alt=\"GO_package\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ed:/gotest/src/main/main.go\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage main\n\nimport (\n\u0026#34;fmt\u0026#34;\n\u0026#34;stu\u0026#34;\n)\n\nfunc main() {\n\n//sxf := new(stu.Stu)\nsxf := \u0026amp;stu.Stu{}\n\nsxf.SetName(\u0026#34;zhangli\u0026#34;)\na := sxf.GetName()\n\nfmt.Println(a)\n}\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e\u003cstrong\u003ed:/gotest/src/stu/stu.go\u003c/strong\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003epackage stu\n\ntype Stu struct {\n\tname string\n\t//age int\n}\n\nfunc (s *Stu) SetName(name string) {\n\ts.name = name\n} …\u003c/code\u003e\u003c/pre\u003e"
April 23, 2013
golang中实现自定义数据类型struct
"\u003cp\u003e可以参考: \u003ca href=\"http://blog.haohtml.com/archives/13556\"\u003egolang中的函数\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003efunc.go\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e (\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;fmt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003etype\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estu\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estruct\u003c/span\u003e {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eName\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e//首字母大写,允许其它包直接使用,可以直接使用 stu.Name = \u0026#39;test\u0026#39; 也可以使用 setName和getName\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003eint\u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e//不允许外面的包使用,可以使用 setAge和getAge方法\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eperl\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e new(\u003cspan style=\"color:#a6e22e\"\u003estu\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eperl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eName\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;zhang\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e// age\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esetAge\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eperl\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e30\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003egetAge\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003eperl\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efmt\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePrintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%v\\n\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003eage\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//name\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003evar\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003estring\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eperl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003esetName\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;sun\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e = \u003cspan style=\"color:#a6e22e\"\u003eperl\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003egetName\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efmt\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePrintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%i\\n\u0026#34;\u003c/span\u003e, \u003cspan style=\"color:#a6e22e\"\u003ename\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e//print struct\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003efmt\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePrintf\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;%v\\n\u0026#34;\u003c/span\u003e, …\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e"
December 31, 2012
golang中的函数
"\u003cp\u003e函数是构建Go程序的基础部件;所遇有趣的事情都是在它其中发生的。函数\n的定义看起来像这样:\nListing 3.1. 函数定义\u003c/p\u003e\n\u003cp\u003etype mytype int 新的类型,参阅第 5 章\n\u003ca href=\"http://blog.haohtml.com/wp-content/uploads/2012/12/golang-func.png\"\u003e\u003cimg src=\"http://blog.haohtml.com/wp-content/uploads/2012/12/golang-func.png\" alt=\"golang-func\"\u003e\u003c/a\u003e\n0 保留字func用于定义一个函数;\u003c/p\u003e\n\u003cp\u003e1 函数可以定义用于特定的类型,这类函数更加通俗的称呼是method。这\n部分称作receiver而它是可选的(可参考: \u003ca href=\"http://blog.haohtml.com/archives/13766\"\u003ehttp://blog.haohtml.com/archives/13766\u003c/a\u003e)。如下图:\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://blog.haohtml.com/wp-content/uploads/2012/12/golang-struct-func.png\"\u003e\u003cimg src=\"http://blog.haohtml.com/wp-content/uploads/2012/12/golang-struct-func.png\" alt=\"golang-struct-func\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e2 funcname是你函数的名字;\n3 int类型的变量q作为输入参数。参数用pass-by-value方式传递,意味着它\n们会被复制;\n4 变量r和s是这个函数的命名返回值。在Go的函数中可以返回多个值。\n参阅第32页的“多值返回”。如果不想对返回的参数命名,只需要提供类\n型:(int,int)。如果只有一个返回值,可以省略圆括号。如果函数是一\n个子过程,并且没有任何返回值,也可以省略这些内容;\n5 这是函数体,注意return是一个语句,所以包裹参数的括号是可选的。\u003c/p\u003e\n\u003cp\u003e这里有两个例子,左边的函数没有返回值,右边的只是简单的将输入返回。 …\u003c/p\u003e"
December 27, 2012
[golang]将函数作为值
"\u003cp\u003e就像其它在Go中的几乎所有的东西,函数也同样是值而已.它们可以像下面这样赋值给变量:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003epackage\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;fmt\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003emain\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#a6e22e\"\u003ef\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e:=\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003efunc\u003c/span\u003e() {\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\t \u003cspan style=\"color:#a6e22e\"\u003efmt\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ePrintln\u003c/span\u003e(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;func\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e }\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#75715e\"\u003e// 下面才开始调用函数\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#a6e22e\"\u003ef\u003c/span\u003e()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e}\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e结果会打印出 func 字符串。\u003c/p\u003e\n\u003cp\u003e另一种用法是立即调用函数,但是要求匿名函数要有返回值才可以,不然会提示错误信息.\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://blog.haohtml.com/wp-content/uploads/2012/12/golang_func.jpg\"\u003e\u003cimg src=\"https://blogstatic.haohtml.com//uploads/2023/09/golang_func.jpg\" alt=\"golang_func\"\u003e\u003c/a\u003e\u003c/p\u003e"
December 15, 2012
windows 下搭建 GoLang 语言开发环境
"\u003cp\u003egolang官方二进制分发包包括FreeBSD, Linux, Mac OS X (Snow Leopard/Lion), and Windows等平台,包括32位、64位等版本。\u003c/p\u003e\n\u003cp\u003e我自己使用的是windows 32位分发包,MSI格式的,下载地址为: \u003ca href=\"http://code.google.com/p/go/downloads/list\"\u003ehttp://code.google.com/p/go/downloads/list\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003egolang支持交叉编译,也就是说你在32位平台的机器上开发,可以编译生成64位平台上的可执行程序。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e环境变量说明:\u003c/strong\u003e\n$GOROOT 指向golang安装之后的根目录,windows平台下默认为c:\\go,会在安装过程中由安装程序自动写入系统环境变量。\n$GOARCH 目标平台(编译后的目标平台)的处理器架构(386、amd64、arm)\n$GOOS 目标平台(编译后的目标平台)的操作系统(darwin、freebsd、linux、windows)\n$GOBIN 指向安装之后根目录下的bin目录,即$GOROOT/bin,windows平台下默认为c:\\go\\bin,会在安装过程中由安装程序自动添加到PATH变量中\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e配 …\u003c/strong\u003e\u003c/p\u003e"