Below you will find pages that utilize the taxonomy term “golang”
January 16, 2023
Golang 中网络请求使用指定网卡
"当用户发起一个网络请求时,流量会通过默认的网卡接口流出与流入,但有时需要将流量通过指定的网卡进行流出流入,这时我们可能需要进行一些额外的开发工作,对其实现主要用到了 Dialer.Control 配置项。\ntype Dialer struct { // If Control is not nil, it is called after creating the network // connection but before actually dialing. // // Network and address parameters passed to Control method are not // necessarily the ones passed to Dial. For example, passing \u0026#34;tcp\u0026#34; to Dial // will cause the Control function to be called with \u0026#34;tcp4\u0026#34; or \u0026#34;tcp6\u0026#34;. Control func(network, …"
February 25, 2022
一文看懂Golang 定时器源码
"计时器分 Timer 和 Ticker 两种,它们底层基本是一样的,两差的区别请参考 , 这里我们的介绍对象是 Timer 。golang timer\n计时器结构体 // NewTimer creates a new Timer that will send // the current time on its channel after at least duration d. func NewTimer(d Duration) *Timer { c := make(chan Time, 1) t := \u0026amp;Timer{ C: c, r: runtimeTimer{ when: when(d), f: sendTime, arg: c, }, } startTimer(\u0026amp;t.r) return t } 通过调用 NewTimer() 函数创建一个 Timer,首先创建一个长度为1的有缓冲channel,再创建一个Timer的结构体,并将 channel 置于 Timer 结构体内。\n注意这里的 runtimeTimer."
November 25, 2021
Golang常见编译参数
"在执行 go build 命令的时候,经常需要添加一些参数,或许是为了调试,也或许是为了生成最终部署二进制文件。\n在编译特定包时需要传递参数,格式应遵守“包名=参数列表”,如\ngo build -gcflags -gcflags=\u0026#39;log=-N -l\u0026#39; main.go -gcflags go build 可以用 -gcflags 给_go_编译器传入参数,也就是传给 go tool compile 的参数,因此可以用 go tool compile –help 查看所有可用的参数。\n其中 -m 可以检查代码的编译优化情况,包括逃逸情况和函数是否内联。\n-ldflags go build用 -ldflags 给go链接器传入参数,实际是给go tool link的参数,可以用go tool link –help查看可用的参数。\n常用-X来指定版本号等编译时才决定的参数值。例如代码中定义var buildVer string,然后在编译时用go build -ldflags “-X main.buildVer=1.0” … 来赋值。注意-X只能给string类型变量赋值。"
November 25, 2021
Golang中的 CGO_ENABLED 环境变量
"Golang中的编译参数\n开发中经常使用 go build 命令来编译我们的程序源码,然后将生成二进制文件直接部署,极其方便。\n对于 go build 有一些参数,对于针对程序源码进行一些编译优化,下面我们对经常使用的一些参数来介绍一下。\n环境变量 环境变量需要在go命令前面设置,如果多个变量的话,中间需要用“空格”分隔。下面我们介绍一个非常常见到的一些环境变量\n$ CGO_ENABLED=1 GOARCH=amd64 GOOS=linux go build -o myserver main.go 除了这里给出的这几个变量外,还有一些其它变量,如 GODEBUG、GOFLAGS、GOPROXY 等,所有支持环境变量都可以在 里找到,有兴趣的话可以看看他们的作用。\n这里重点介绍一下 CGO_ENABLED 环境变量对我们程序的影响。 CGO_ENABLED是用来控制golang 编译期间是否支持调用 cgo 命令的开关,其值为1或0,默认情况下值为1,可以用 go env 查看默认值。\n如果你的程序里调用了cgo 命令,此参数必须设置为1,否则将编译时出错。这里直接用文档 中的一个例子验 …"
May 23, 2021
Golang中的runtime.LockOSThread 和 runtime.UnlockOSThread
"在runtime中有 [runtime.LockOSThread](https://github.com/golang/go/blob/go1.16.3/src/runtime/proc.go#L4248-L4278) 和 [runtime.UnlockOSThread](https://github.com/golang/go/blob/go1.16.3/src/runtime/proc.go#L4302-L4323) 两个函数,这两个函数有什么作用呢?我们看一下标准库中对它们的解释。\nruntime.LockOSThread // LockOSThread wires the calling goroutine to its current operating system thread. // The calling goroutine will always execute in that thread, // and no other goroutine will execute in it, // until the calling goroutine has made …"
May 10, 2021
Runtime: goroutine的暂停和恢复源码剖析
"上一节《 GC 对根对象扫描实现的源码分析》中,我们提到过在GC的时候,在对一些goroutine 栈进行扫描时,会在其扫描前触发 G 的暂停([suspendG](https://github.com/golang/go/blob/go1.16.2/src/runtime/preempt.go#L76-L254))和恢复([resumeG](https://github.com/golang/go/blob/go1.16.2/src/runtime/preempt.go#L256-L280))。\n// markroot scans the i\u0026#39;th root. // // Preemption must be disabled (because this uses a gcWork). // // nowritebarrier is only advisory here. // //go:nowritebarrier func markroot(gcw *gcWork, i uint32) { baseFlushCache := uint32(fixedRootCount) …"
May 7, 2021
goroutine栈的申请与释放
"对于提高对 stack 的使用效率,避免重复从heap中分配与释放,对其使用了 pool 的概念,runtime 里为共提供了两个pool, 分别为 stackpool ,另一个为 stackLarge。stack pool\nstackpool: 16b~32k 对应通用的大小的stack。获取时通过调用 stackpoolalloc(), 释放时调用 stackpoolfree()。\nstackLarge:对应 \u0026gt; 32K 的 stack\n在程序全局调度器 初始化 时会通过调用 stackinit() 实现对 stack 初始化。\n当我们执行一个 go func() 语句的时候,runtime 会通过调用 newproc() 函数来创建G。而内部真正创建G的函数为 [newproc1()](https://github.com/golang/go/blob/go1.16.3/src/runtime/proc.go#L3990-L4098),在没有G可以复用的情况下,会通过 newg = malg(_StackMin) 语句创建一个包含stack的G。\n// Allocate a …"
May 7, 2021
Golang的GPM 模型在网络编程中存在的问题
"现状 目前在网络编程中,golang采用的是一种 goroutine-per-connection 的模式,即为每一个连接都分配一个goroutine,一个连接就是一个goroutine,多个连接之间没有关系。\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;io/ioutil\u0026#34; \u0026#34;net\u0026#34; \u0026#34;time\u0026#34; ) //模拟server端 func main() { tcpServer, _ := net.ResolveTCPAddr(\u0026#34;tcp4\u0026#34;, \u0026#34;:8080\u0026#34;) listener, _ := net.ListenTCP(\u0026#34;tcp\u0026#34;, tcpServer) for { //当有新客户端请求时拿到与客户端的连接 conn, err := listener.Accept() if err != nil { fmt.Println(err) continue } // 处理逻辑 goroutine-per-connection go handle(conn) } } …"
April 30, 2021
缓存池 bytebufferpool 库实现原理
"上一节 《Runtime: Golang 之 sync.Pool 源码分析》 我们介绍了sync.Pool 的源码分析,本节介绍一个 fasthttp 中引用的一缓存池库 [bytebufferpool](https://github.com/valyala/bytebufferpool),这两个库是同一个开发者。对于这个缓存池库与同类型的几个库的对比,可以参考 https://omgnull.github.io/go-benchmark/buffer/。\n建议大家了解一下[fasthttp](https://github.com/valyala/fasthttp) 这个库,性能要比直接使用内置的 net/http 高出很多,其主要原因是大量的用到了缓存池 sync.Pool 进行性能提升。\n用法 // https://github.com/valyala/bytebufferpool/blob/18533face0/bytebuffer_example_test.go package bytebufferpool_test import ( \u0026#34;fmt\u0026#34; …"
April 12, 2021
Golang 内存组件之mspan、mcache、mcentral 和 mheap 数据结构
"Golang中的内存组件关系如下图所示golang 内存分配组件\n在学习golang 内存时,经常会涉及几个重要的数据结构,如果不熟悉它们的情况下,理解起来就显得格外的吃力,所以本篇主要对相关的几个内存组件做下数据结构的介绍。\n在 Golang 中,mcache、mspan、mcentral 和 mheap 是内存管理的四大组件,mcache 管理线程在本地缓存的 mspan,而 mcentral 管理着全局的 mspan 为所有 mcache 提供所有线程。\n根据分配对象的大小,内部会使用不同的内存分配机制,详细参考函数 mallocgo() ,所于内存分配与回收,参考文件介绍 malloc.go\n\u0026lt;16KB 会使用微小对象内存分配器从 P 中的 mcache 分配,主要使用 mcache.tinyXXX 这类的字段 16-32KB 从 P 中的 mcache 中分配 \u0026gt;32KB 直接从 mheap 中分配 对于golang中的内存申请流程,大家应该都非常熟悉了,这里不再进行详细描述。Golang 内存组件关系\nmcache 在GPM关系中,会在每个 P …"
April 9, 2021
GC 对根对象扫描实现的源码分析
"工作池gcWork 工作缓存池(work pool)实现了生产者和消费者模型,用于指向灰色对象。一个灰色对象在工作队列中被扫描标记,一个黑色对象表示已被标记不在队列中。\n写屏障、根发现、栈扫描和对象扫描都会生成一个指向灰色对象的指针。扫描消费时会指向这个灰色对象,从而将先其变为黑色,再扫描它们,此时可能会产生一个新的指针指向灰色对象。这个就是三色标记法的基本知识点,应该很好理解。\ngcWork 是为垃圾回收器提供的一个生产和消费工作接口。\n它可以用在stack上,如\n(preemption must be disabled) gcw := \u0026amp;getg().m.p.ptr().gcw .. call gcw.put() to produce and gcw.tryGet() to consume .. 在标记阶段使用gcWork可以防止垃圾收集器转换到标记终止,这一点很重要,因为gcWork可能在本地持有GC工作缓冲区。可以通过禁用抢占(systemstack 或 acquirem)来实现。\n数据结构\ntype gcWork struct { wbuf1, wbuf2 …"
April 6, 2021
Golang中的切片与GC
"今天再看 timer 源码的时候,在函数 [clearDeletedTimers()](https://github.com/golang/go/blob/go1.16.2/src/runtime/time.go#L904-L992) 里看到一段对切片的处理代码,实现目的就是对一个切片内容进行缩容。\n// src/runtime/time.go // The caller must have locked the timers for pp. func clearDeletedTimers(pp *p) { timers := pp.timers ...... // 对无用的切片元素赋值 nil for i := to; i \u0026lt; len(timers); i++ { timers[i] = nil } atomic.Xadd(\u0026amp;pp.deletedTimers, -cdel) atomic.Xadd(\u0026amp;pp.numTimers, -cdel) atomic.Xadd(\u0026amp;pp.adjustTimers, -cearlier) timers = …"
March 29, 2021
Runtime: Golang 定时器实现原理及源码解析
"定时器作为开发经常使用的一种数据类型,是每个开发者需要掌握的,对于一个高级开发很有必要了解它的实现原理,今天我们runtime源码来学习一下它的底层实现。\n定时器分两种,分别为 Timer 和 Ticker,两者差不多,这里重点以Timer为例。\n源文件位于 [src/time/sleep.go](https://github.com/golang/go/blob/go1.16.2/src/time/sleep.go) 和 [src/time/tick.go](https://github.com/golang/go/blob/go1.16.2/src/time/tick.go) 。 go version 1.16.2\n数据结构 Timer 数据结构\n// src/runtime/sleep.go // The Timer type represents a single event. // When the Timer expires, the current time will be sent on C, // unless the Timer was created by …"
March 28, 2021
Golang中的CAS原子操作 和 锁
"在高并发编程中,经常会出现对同一个资源并发访问修改的情况,为了保证最终结果的正确性,一般会使用 锁 和 CAS原子操作 来实现。\n如要对一个变量进行计数统计,两种实现方式分别为\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; ) // 锁实现方式 func main() { var count int64 var wg sync.WaitGroup var mu sync.Mutex for i := 0; i \u0026lt; 10000; i++ { wg.Add(1) go func(wg *sync.WaitGroup) { defer wg.Done() mu.Lock() count = count + 1 mu.Unlock() }(\u0026amp;wg) } wg.Wait() // count = 10000 fmt.Println(\u0026#34;count = \u0026#34;, count) } 与\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; …"
March 23, 2021
Golang并发同步原语之-信号量Semaphore
"信号量是并发编程中比较常见的一种同步机制,它会保持资源计数器一直在0-N(N表示权重值大小,在用户初始化时指定)之间。当用户获取的时候会减少一点,使用完毕后再恢复过来。当遇到请求时资源不够的情况下,将会进入休眠状态以等待其它进程释放资源。\n在 Golang 官方扩展库中为我们提供了一个基于权重的信号量 [semaphore](https://github.com/golang/sync/blob/master/semaphore/semaphore.go) 并发原语。\n你可以将下面的参数 n 理解为资源权重总和,表示每次获取时的权重;也可以理解为资源数量,表示每次获取时必须一次性获取的资源数量。为了理解方便,这里直接将其理解为资源数量。\n数据结构 [semaphoreWeighted](https://github.com/golang/sync/blob/master/semaphore/semaphore.go#L19-L33) 结构体\ntype waiter struct { n int64 ready chan\u0026lt;- struct{} // Closed when …"
March 22, 2021
学习Golang GC 必知的几个知识点
"对于gc的介绍主要位于 [src/runtime/mgc.go](https://github.com/golang/go/blob/go1.16.2/src/runtime/mgc.go),以下内容是对注释的翻译。\nGC 四个阶段 通过源文件注释得知GC共分四个阶段:\nGC 清理终止 (GC performs sweep termination) a. Stop the world, 每个P 进入GC safepoint(安全点),从此刻开始,万物静止。 b. 清理未被清理的span,如果GC被强制执行时才会出现这些未清理的span GC 标记阶段(GC performs the mark phase) a. 将gc标记从 _GCoff 修改为 _GCmark,开启写屏障(write barries)和 协助助手(mutator assists),将根对象放入队列。 在STW期间,在所有P都启用写屏障之前不会有什么对象被扫描。 b. Start the world(恢复STW)。标记工作线程和协助助手并发的执行。对于任何指针的写操作和指针值,都会被写屏障覆盖,使新分配的对象标记为黑 …"
March 20, 2021
Runtime: Golang 之 sync.Pool 源码分析
"Pool 指一组可以单独保存和恢复的 临时对象。Pool 中的对象随时都有可能在没有收到任何通知的情况下被GC自动销毁移除。\n多个goroutine同时操作Pool是并发安全的。\n源文件为 [src/sync/pool.go](https://github.com/golang/go/blob/master/src/sync/pool.go) go version: 1.16.2\n为什么使用Pool 在开发高性能应用时,经常会有一些完全相同的对象需要频繁的创建和销毁,每次创建都需要在堆中分配对象,等使用完毕后,这些对象需要等待GC回收。我们知道在Golang中使用三色标记法进行垃圾回收的,在回收期间会有一个短暂STW(stop the world)的时间段,这样就会导致程序性能下降。\n那么能否实现类似数据库连接池这种效果,用来避免对象的频繁创建和销毁,达到尽可能的资源复用呢?为了实现这种需求,标准库中有了sync.Pool 这个数据结构。看名字很知道它是一个池。但是它和我们想象中的数据库连接池还是有些差别的。对于数据库连接池这种资源只要不手动释放就可以一直利用, …"
March 19, 2021
Runtime: Golang同步原语Mutex源码分析
"在 sync 包里提供了最基本的同步原语,如互斥锁 Mutex。除 Once 和 WaitGroup 类型外,大部分是由低级库提供的,更高级别的同步最好是通过 channel 通讯来实现。\nMutex 类型的变量默认值是未加锁状态,在第一次使用后,此值将不得复制,这点切记!!!\n本文基于go version: 1.16.2\nMutex 锁实现了 Locker 接口。\n// A Locker represents an object that can be locked and unlocked. type Locker interface { Lock() Unlock() } 锁的模式 为了互斥公平性,Mutex 分为 正常模式 和 饥饿模式 两种。\n正常模式 在正常模式下,等待者 waiter 会进入到一个FIFO队列,在获取锁时waiter会按照先进先出的顺序获取。当唤醒一个waiter 时它被并不会立即获取锁,而是要与新来的goroutine竞争,这种情况下新来的goroutine比较有优势,主要是因为它已经运行在CPU,可能它的数量还不少,所以waiter大概率下获取不到锁。 …"
March 5, 2021
Golang什么时候会触发GC
"Golang采用了三色标记法来进行垃圾回收,那么在什么场景下会触发这个GC动作呢?\n源码主要位于文件 [src/runtime/mgc.go](https://github.com/golang/go/blob/go1.16/src/runtime/mgc.go) go version 1.16\n触发条件从大方面来说,分为 手动触发 和 系统触发 两种方式。手动触发一般很少用,主要通过开发者调用 runtime.GC() 函数来实现,而对于系统自动触发是 运行时 根据一些条件自行维护的,这也正是本文要介绍的内容。\n不管哪种触发方式,底层回收机制是一样的,所以我们先看一下手动触发,看看能否根据它来找GC触发所需的条件。\n// src/runtime/mgc.go // GC runs a garbage collection and blocks the caller until the // garbage collection is complete. It may also block the entire // program. func GC() { n := …"
March 4, 2021
Golang 基于信号的异步抢占与处理
"在Go1.14版本开始实现了 基于信号的协程抢占调度 模式,在此版本以前执行以下代码是永远也无法执行最后一条println语句。\n本文基于go version 1.16\npackage main import ( \u0026#34;runtime\u0026#34; \u0026#34;time\u0026#34; ) func main() { runtime.GOMAXPROCS(1) go func() { for { } }() time.Sleep(time.Millisecond) println(\u0026#34;OK\u0026#34;) } 原因很简单:在main函数里只有一个CPU,从上到下执行到 time.Sleep() 函数的时候,会将 main goroutine 放入运行队列,出让了P,开始执行匿名函数,但匿名函数是一个for循环,没有任何 IO 语句,也就无法引起对 G 的调度,所以当前仅有的一个 P 永远被其占用,导致无法打印OK。\n这个问题在1.14版本开始有所改变,主要是因为引入了基于信号的抢占模式。在程序启动时,初始化信号,并在 runtime.sighandler 函数注册了 SIGURG 信号的处理 …"
March 1, 2021
Golang 的调度策略之G的窃取
"我们上篇文章( Golang 的底层引导流程/启动顺序)介绍了一个golang程序的启动流程,在文章的最后对于最重要的一点“调度“ (函数 [schedule()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L2607-L2723)) 并没有展开来讲,今天我们继续从源码来分析一下它的调度机制。\n在此之前我们要明白golang中的调度主要指的是什么?在 src/runtime/proc.go 文件里有一段注释这样写到\n// Goroutine scheduler\n// The scheduler’s job is to distribute ready-to-run goroutines over worker threads.\n这里指如何找一个已准备好运行的 G 关联到PM 让其执行。对于G 的调度可以围绕三个方面来理解:\n时机:什么时候关联(调度)。对于调度时机一般是指有空闲P的时候都会去找G执行 对象:选择哪个G进行调度。这是我们本篇要讲的内容 机制:如何调度。execute() 函数 理解了这三个 …"
February 27, 2021
Runtime: Golang是如何处理系统调用阻塞的?
"我们知道在Golang中,当一个Goroutine由于执行 系统调用 而阻塞时,会将M从GPM中分离出去,然后P再找一个G和M重新执行,避免浪费CPU资源,那么在内部又是如何实现的呢?今天我们还是通过学习Runtime源码的形式来看下他的内部实现细节有哪些?\ngo version 1.15.6\n我们知道一个P有四种运行状态,而当执行系统调用函数阻塞时,会从 _Prunning 状态切换到 _Psyscall,等系统调用函数执行完毕后再切换回来。P的状态切换\n从上图我们可以看出 P 执行系统调用时会执行 [entersyscall()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3134-L3142) 函数(另还有一个类似的阻塞函数 entersyscallblock() ,注意两者的区别)。当系统调用执行完毕切换回去会执行 exitsyscall() 函数,下面我们看一下这两个函数的实现。\n进入系统调用 // Standard syscall entry used by the go syscall …"
February 26, 2021
Runtime: 当一个goroutine 运行结束后会发生什么
"上一篇我们介绍了 创建一个goroutine 会经历些什么,今天我们再看下当一个goroutine 运行结束的时候,又会发生什么?\ngo version 1.15.6。\n主要源文件为 [src/runtime/proc.go](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go)。\n当一个goroutine 运行结束的时候,默认会执行一个 [goexit1()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L2941-L2950) 的函数,这是一个只有八行代码的函数,其中最后以通过 [mcall()](https://github.com/golang/go/blob/go1.15.6/src/runtime/stubs.go#L34) 调用 [goexit0](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L2952-L3011) 函数结束。因此我们主 …"
February 17, 2021
Runtime: 创建一个goroutine都经历了什么?
"我们都知道goroutine的在golang中发挥了很大的作用,那么当我们创建一个新的goroutine时,它是怎么一步一步创建的呢?都经历了哪些操作呢?今天我们通过源码来剖析一下创建goroutine都经历了些什么?go version 1.15.6\n对goroutine最关键的两个函数是 [newproc()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3535-L3564) 和 [newproc1()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3566-L3674),而 newproc1() 函数是我们最需要关注的。\n函数 newproc() 我们先看一个简单的创建goroutine的例子,找出来创建它的函数。\npackage main func start(a, b, c int64) { _ = a + b + c } func main() { go start(7, 2, 5) } 输出结果:\n➜ …"
February 15, 2021
Runtime: 理解Golang中接口interface的底层实现
"接口类型是Golang中是一种非常非常常见的数据类型,每个开发人员都很有必要知道它到底是如何使用的,如果了解了它的底层实现就对开发就更有帮助了。\n接口的定义 在Golang中 interface 通常是指实现了一 组抽象方法的集合,它提供了一种无侵入式的方式。当你实现了一个接口中指定的所有方法的时候,那么就实现了这个接口,在Golang中对它的实现并不需要 implements 关键字。\n有时候我们称这种模型叫做鸭子模型(Duck typing),维基百科对鸭子模型的定义是\n”If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.“\n翻译过来就是 ”如果它看起来像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那他就可以认为是鸭子“。\nGo 不同版本之间interface的结构可能不太一样,但整体都差不多,这里使用的Go版本为 1.15.6。\n数据结构 Go 中 interface 在运行时可分 eface 和 iface 两种数据结构,我们先看一下对它们的定 …"
February 13, 2021
认识Golang中的sysmon监控线程
"Go Runtime 在启动程序的时候,会创建一个独立的 M 作为监控线程,称为 sysmon,它是一个系统级的 daemon 线程。这个sysmon 独立于 GPM 之外,也就是说不需要P就可以运行,因此官方工具 go tool trace 是无法追踪分析到此线程( 源码)。sysmon\n在程序执行期间 sysmon 每隔 20us~10ms 轮询执行一次( 源码),监控那些长时间运行的 G 任务, 然后设置其可以被强占的标识符,这样别的 Goroutine 就可以抢先进来执行。\n// src/runtime/proc.go // forcegcperiod is the maximum time in nanoseconds between garbage // collections. If we go this long without a garbage collection, one // is forced to run. // // This is a variable for testing purposes. It normally doesn\u0026#39;t …"
February 11, 2021
g0 特殊的goroutine
"在上篇 《golang中G、P、M 和 sched 三者的数据结构》文章中,我们介绍了G、M 和 P 的数据结构,其中M结构体中第一个字段是 g0,这个字段也是一个 goroutine,但和普通的 goroutine 有一些区别,它主要用来实现对 goroutine 进行调度,下面我们将介绍它是如何实现调度goroutine的。\n另外还有一个 m0 , 它是一个全局变量,与 g0 的区别如下M0 与 g0的区别\n本文主要翻译自 Go: g0, Special Goroutine 一文,有兴趣的可以查阅原文,作者有一系列高质量的文章推荐大家都阅读一遍。ℹ️ 本文基于 Go 1.13。\n我们知道在Golang中所有的goroutine的运行都是由调度器来负责管理的,go调度器尝试为所有的goroutine来分配运行时间,当有goroutine被阻塞或终止时,调度器会通过对goroutine 进行调度以此来保证所有CPU都处于忙碌状态,避免有CPU空闲状态浪费时间。\ngoroutine 切换规则 在此之前我们需要记住一些goroutine切换规则。runtime源码\n// …"
January 26, 2021
Golang环境变量之GODEBUG
"GODEBUG 是 golang中一个控制runtime调度变量的变量,其值为一个用逗号隔开的 name=val对列表,常见有以下几个命名变量。\nallocfreetrace 设置allocfreetrace = 1会导致对每个分配进行概要分析,并在每个对象的分配上打印堆栈跟踪并释放它们。\nclobberfree 设置 clobberfree=1会使垃圾回收器在释放对象的时候,对象里的内存内容可能是错误的。\ncgocheck cgo相关。\n设置 cgocheck=0 将禁用当包使用cgo非法传递给go指针到非go代码的检查。如果值为1(默认值)会启用检测,但可能会丢失有一些错误。如果设置为2的话,则不会丢失错误。但会使程序变慢。\nefence 设置 efence=1会使回收器运行在一个模式。每个对象都在一个唯一的页和地址,且永远也不会被回收。\ngccheckmark GC相关。\n设置 gccheckmark=1 启用验证垃圾回收器的并发标记,通过在STW时第二个标记阶段来实现,如果在第二阶段的时候,找到一个可达对象,但未找到并发标记,则GC会发生Panic。\ngcpacertrace …"
January 26, 2021
Golang中MemStats的介绍
"平时在开发中,有时间需要通过查看内存使用情况来分析程序的性能问题,经常会使用到 MemStats 这个结构体。但平时用到的都是一些最基本的方法,今天我们全面认识一下MemStas。\n相关文件为 src/runtime/mstats.go ,本文章里主要是与内存统计相关。\nMemStats 结构体 // MemStats记录有关内存分配器的统计信息 type MemStats struct { // General statistics. Alloc uint64 TotalAlloc uint64 Sys uint64 Lookups uint64 Mallocs uint64 Frees uint64 // Heap memory statistics. HeapAlloc uint64 HeapSys uint64 HeapIdle uint64 HeapInuse uint64 HeapReleased uint64 HeapObjects uint64 // Stack memory statistics. StackInuse uint64 StackSys uint64 …"
January 25, 2021
Golang中Stack的管理
"栈的演变 在 Go1.13之前的版本,Golang 栈管理是使用的分段栈(Segment Stacks)机制来实现的,由于sgement stack 存在 热分裂(hot split)的问题,后面版本改为采用连续栈( [Contiguous stacks](https://docs.google.com/document/d/1wAaf1rYoM4S4gtnPh0zOlGzWtrZFQ5suE8qr2sD8uWQ/pub))机制( 说明)。\n分段栈(Segment Stack) 分段栈是指开始时只有一个stack,当需要更多的 stack 时,就再去申请一个,然后将多个stack 之间用双向链接连接在一起。当使用完成后,再将无用的 stack 从链接中删除释放内存。segment stack\n可以看到这样确实实现了stack 按需增长和收缩,在增加新stack时不需要拷贝原来的数据,系统使用率挺高的。但在一定特别的情况下会存在 热分裂(hot split) 的问题。\n当一个 stack 即将用完的时候,任意一个函数都会导致堆栈的扩容,当函数执行完返回后,又要触发堆栈的收缩。如果这个操作 …"
January 22, 2021
Golang 的底层引导流程/启动顺序
"在Golang中,程序的执行入口为 main() 函数,那么底层又是如何工作的呢? 这个问题的答案我们可以在runtime源码找到。对它的解释主要在 [src/runtime/proc.go](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go) 文件,下面我们看一下它是如何一步一步开始执行的。go version 1.15.6\n在文件头部有一段对 [Goroutine scheduler](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L19) 的介绍,我们先了解一下。\n调度器的工作是分发goroutines到工作线程让其运行。一句话指明了调度器的存在意义,就是指挥协调GPM干活。\n主要包含三部分 G 指的是 goroutine M 工作线程,也叫machine P 处理器(逻辑CPU),执行 Go code 的一种资源。这里的Go code 其实就是 goroutine里的代码。\nM必须被指派给P去执行 Go code, 但可以被 …"
January 21, 2021
golang中G、P、M 和 sched 三者的数据结构
"G、P、M 三者是golang实现高并发能的最为重要的概念,runtime 通过 调度器 来实现三者的相互调度执行,通过 p 将用户态的 g 与内核态资源 m 的动态绑定来执行,以减少以前通过频繁创建内核态线程而产生的一系列的性能问题,充分发挥服务器最大有限资源。GPM 协作\n调度器的工作是将一个 G(需要执行的代码)、一个 M(代码执行的地方)和一个 P(代码执行所需要的权限和资源)结合起来。\n所有的 g、m 和 p 对象都是分配在堆上且永不释放的,所以它们的内存使用是很稳定的。得益于此,runtime 可以在调度器实现中避免写屏障。当一个G执行完成后,可以放入pool中被再次使用,避免重复申请资源。\n本节主要通过阅读runtime源码来认识这三个组件到底长的是什么样子,以此加深对 GPM 的理解。go version go1.15.6\n理解下文前建议先阅读一下 src/runtime/HACKING.md 文件,中文可阅读 这里,这个文件内容是面向开发者理解runtime的很值得看一看。\n本文若没有指定源码文件路径,则默认为 src/runtime/runtime2.go。\nG G …"
January 18, 2021
Runtime: Golang中channel实现原理源码分析
"channel是golang中特有的一种数据结构,通常与goroutine一起使用,下面我们就介绍一下这种数据结构。\nchannel数据结构 channel 是Golang 中最重要的一个数据结构,源码里对应的结构体是hchan,当我们创建一个channel 的时候,实际上是创建了一个hchan结构体。\nhchan结构体 // src/runtime/chan.go type hchan struct { qcount uint // total data in the queue dataqsiz uint // size of the circular queue buf unsafe.Pointer // points to an array of dataqsiz elements elemsize uint16 closed uint32 elemtype *_type // element type sendx uint // send index recvx uint // receive index recvq waitq // list of recv waiters …"
January 11, 2021
Runtime:源码解析Golang 的map实现原理
"go version 1.15.6\nmap作为一种常见的 key-value 数据结构,不同语言的实现原理基本差不多。首先在系统里分配一段连接的内存地址作为数组,然后通过对map键进行hash算法(最终将键转换成了一个整型数字)定位到不同的桶bucket(数组的索引位置),然后将值存储到对应的bucket里\n理想的情况下是一个bucket存储一个值,即数组的形式,时间复杂度为O(1)。\n如果存在键值碰撞的话,可以通过 链表法 或者 开放寻址法 来解决。\n链表法\n开放寻址法\n对于开放寻址法有多种算法,常见的有线性探测法,线性补偿探测法,随机探测法等,这里不再介绍。\nmap基本数据结构 hmap结构体 map的核心数据结构定义在 /runtime/map.go\n// A header for a Go map. type hmap struct { // Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go. // Make sure this stays in sync …"
December 26, 2020
Golang并发模式之扇入FAN-IN和扇出FAN-OUT
"在现实世界中,经常有一些工作是属于流水线类型的,它们的每一个步骤都是紧密关联的,第一步先做什么,再做什么,最后做什么。特别是制造业这个行业,基本全是流水线生产车间。在我们开发中也经常遇到这类的业务场景。\n假如我们有个流水线共分三个步骤,分别是 job1、job2和job3。代码: https://play.golang.org/p/e7ZlP9ofXB3\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func job1(count int) \u0026lt;-chan int { outCh := make(chan int, 2) go func() { defer close(outCh) for i := 0; i \u0026lt; count; i++ { time.Sleep(time.Second) fmt.Println(\u0026#34;job1 finish:\u0026#34;, 1) outCh \u0026lt;- 1 } }() return outCh } func job2(inCh \u0026lt;-chan int) \u0026lt;-chan int …"
December 24, 2020
重新认识Golang中的空结构体
"认识空结构体 低层实现原理 空结构体之内存对齐 应用场景 在golang中,如果我们想实现一个set集合的话,一般会使用map来实现,其中将set的值作为map的键,对于map的值一般使用一个空结构体来实现,当然对map值也可以使用一个bool类型或者数字类型等,只要符合一个键值对应关系即可。但我们一般推荐使用struct{}来实现,为什么呢?\npackage main import \u0026#34;fmt\u0026#34; func main() { m := make(map[int]struct{}) m[1] = struct{}{} m[2] = struct{}{} if _, ok := m[1]; ok { fmt.Println(\u0026#34;exists\u0026#34;) } } 上面这段代码是一个很简单的使用map实现的set功能,这里是采用空结构体struct{}来实现。\n在分析为什么使用struct{}以前,我看先认识一个struct。\n认识空结构体 struct 我们先看一个这段代码\npackage main import ( \u0026#34;fmt\u0026#34; …"
December 17, 2020
Golang中的内存重排(Memory Reordering)
"什么是内存重排 内存重排指的是内存的读/写指令重排。\n为什么要内存重排 为了提升程序执行效率,减少一些IO操作,一些硬件或者编译器会对程序进行一些指令优化,优化后的结果可能会导致程序编码时的顺序与代码编译后的先后顺序不一致。\n就拿做饭场景来说吧,是先蒸米还是先炒菜,这两者是没有冲突的,编译器在编译时有可能与你要求的顺序不一样。\n编译器重排 如下面这段代码\nX = 0 for i in range(100): X = 1 print X 要实现打印100次1,很显示在for里面每次都执行X=1语句有些浪费资源,如果将初始变量值修改为1,是不是要快的多。编译器也分析到了这一点,于是在编译时对代码做了以下优化\nX = 1 for i in range(100): print X 最终输出结果是一样的,两段代码功能也一样。\n但是如果此时有另一个线程里执行了一个 X=0 的赋值语句的话(两个线程同时运行),那么输出结果就可能与我们想要的不一样了。\n优化前情况:第一个线程执行到了第3次print X 后,第二个线程执行了X=0,把X 的值进行了修改,结果就有可能是1110 1111(在线程执行第5 …"
November 19, 2020
Golang中的并发原语 Singleflight
"在Golang中有一个并发原语是 Singleflight,好像知道的开发者并不多。其中著名的 https://github.com/golang/groupcache 就用到了这个并发原语。\nGolang版本 go1.15.5\n相关知识点 map、Mutex、channel、\n使用场景 一般用在对指定资源频繁操作的情况下,如高并发下的“缓存击穿”问题。\n缓存击穿:一个存在的key,在缓存过期的瞬间,同时有大量的请求过来,造成所有请求都去DB读取数据,这些请求都会击穿缓存到DB,造成瞬时DB请求量大、压力瞬间骤增,导致数据库负载过高,影响整个系统正常运行。(缓存击穿不同于 缓存雪崩 和 缓存穿透)\n怎么理解这个原语呢,简单的讲就是将对同一个资源的多个请求合并为一个请求。\n举例说明,假如当有10万个请求来获取同一个key的值的时候,正常情况下会执行10万次get操作。而使用singleflight并发语后,只需要首次的地个请求执行一次get操作就可以了,其它请求再过来时,只需要只需要等待即可。待执行结果返回后,再把结果分别返回给等待中的请求,每个请求再返回给客户端,由此看看,在一定的高并 …"
September 19, 2020
Golang开发中中使用GitHub私有仓库
"私有仓库地址为\ngithub.com/cfanbo/websocket 一、设置私有环境变量 GOPRIVATE $ go env -w GOPRIVATE=github.com/cfanbo/websocket 对于为什么需要设置 GOPRIMARY 变量,可以参考 这里\n对于GOPRIVATE值级别分为仓库级别和账号级别。\n如果只有一个仓库,直接设置为仓库地址即可。如果有多个私有仓库的话,使用”,”分开,都在这个账号下,也可以将值设置为账号级别,这样账号下的所有私有仓库都可以正常访问。如 http://github.com/cfanbo\n如果不想每次都重新设置,我们也可以利用通配符,例如:\n$ go env -w GOPRIVATE=\u0026#34;*.example.com\u0026#34; 这样子设置的话,所有模块路径为 example.com 的子域名(例如:git.example.com)都将不经过 Go module proxy 和 Go checksum database,需要注意的是不包括 example.com 本身。\n国内用户访问仓库建议设置 GORPOXY …"
May 27, 2020
golang中几种对goroutine的控制方法
"我们先看一段代码\nfunc listen() { ticker := time.NewTicker(time.Second) for { select { case \u0026lt;-ticker.C: fmt.Println(time.Now()) } } } func main() { go listen() time.Sleep(time.Second * 5) fmt.Println(\u0026#34;main exit\u0026#34;) } 非常简单的一个goroutine用法,想必每个gopher都看过的。\n不过在实际生产中,我们几乎看不到这种用法的的身影,原因很简单,我们无法实现对goroutine的控制,而一般业务中我们需要根据不同情况对goroutine进行各种操作。\n要实现对goroutine的控制,一般有以下两种。\n一、手动发送goroutine控制信号 这里我们发送一个退出goroutine的信号。\n// listen 利用只读chan控制goroutine的退出 func listen(ch \u0026lt;-chan bool) { ticker := …"
May 3, 2020
Golang遍历切片删除元素引起恐慌问题
"删除一个切片的部分元素, 告知切片操作:Golang遍历切片恐慌时删除元素\n问题描述 代码( 演示代码):\npackage main import ( \u0026#34;fmt\u0026#34; ) func main() { slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} for i, value := range slice { if value%3 == 0 { // remove 3, 6, 9 slice = append(slice[:i], slice[i+1:]...) } } fmt.Printf(\u0026#34;%v\u0026#34;, slice) } 运行结果\npanic: runtime error: slice bounds out of range [8:6] goroutine 1 [running]: main.main() /tmp/sandbox2635969259/prog.go:11 +0x212 Program exited. 解决办法: 以下是网友想到的几种办法"
April 30, 2020
Golang中select用法导致CPU占用100%的问题分析
"上一节( golang中有关select的几个知识点)中介绍了一些对于select{}的一些用法,今天介绍一下有关select在 for语句 中由于使用不当引起的CPU占用100% 的案例。\n先看代码\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func main() { ch := make(chan int, 10) // 读取chan go func() { for { select { case i := \u0026lt;-ch: // 只读取15次chan fmt.Println(i) default: // 读取15次chan以后的操作一直在这个空语句无任何IO操作的default条件里死循环,无法出让P,以保证一个GPM关系。 // 而如果无default条件的话,则系统当读取完15次chan后,当前goroutine会发生 chan IO 阻塞, Go调度器根据GPM的调度关系,会将当前执行关系中的G切换出去,再从LRQ队列中取一个新的G,重新组成一个GPM继续执行,以实现合理利用计算机资源,提高GO的高并发性能 } …"
April 21, 2020
基于 GitHub Actions 实现 Golang 项目的自动构建部署
"前几天 GitHub官网宣布 GitHub 的所有核心功能对所有人都免费开放,不得不说自从微软收购了GitHub后,确实带来了一些很大的改变。\n以前有些项目考虑到协作关系的原因,虽然放在github上面,但对于一些项目的持续构建和部署一般是通过自行抢建Travis CI、jenkins等系统来实现。虽然去年推出了Actions用来代替它类三方系统,但感觉着还是不方便,必须有些核心功能无法使用,此消息的发布很有可能将这种格局打破。\n本篇教程将介绍使用github的系列产品来实现项目的发布,构建,测试和部署,当然这仅仅是一个非常小的示例,有些地方后期可能会有更好的瞿恩方案。\nGitHub Actions 是一款持续集成工具,包括clone代码,代码构建,程序测试和项目发布等一系列操作。更多内容参考:\n如果你对CI/CD不了解的话,建议先找些文档看看。\n项目源文件见\nGitHub Actions 术语 GitHub Actions 相关的术语。\n(1)workflow (工作流程):持续集成一次运行的过程,就是一个 workflow。\n(2)job (任务):一个 workflow 由一个或 …"
March 27, 2020
Golang中的限速器 time/rate
"在高并发的系统中,限流已作为必不可少的功能,而常见的限流算法有:计数器、滑动窗口、令牌桶、漏斗(漏桶)。其中滑动窗口算法、令牌桶和漏斗算法应用最为广泛。\n常见限流算法 这里不再对 计数器算法 和 滑动窗口 算法一一介绍,有兴趣的同学可以参考其它相关文章。\n漏斗算法 漏斗算法很容易理解,它就像有一个漏斗容器一样,漏斗上面一直往容器里倒水(请求),漏斗下方以固定速率一直流出(消费)。如果漏斗容器满的情况下,再倒入的水就会溢出,此时表示新的请求将被丢弃。可以看到这种算法在应对大的突发流量时,会造成部分请求弃用丢失。\n可以看出漏斗算法能强行限制数据的传输速率。漏斗算法\n令牌桶算法 从某种意义上来说,令牌算法是对漏斗算法的一种改进。对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发情况。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。\n令牌桶算法是指一个固定大小的桶,可以存放的令牌的最大个数也是固定的。此算法以一种固定速率不断的往桶中存放令牌,而每次请求调用前必须先从桶中获取令牌才可以。否则进行拒绝或等待,直到获取到有效令牌为止。如果桶内的令牌数量已达到桶的最 …"
March 19, 2020
Golang中的两个定时器 ticker 和 timer
"Golang中time包有两个定时器,分别为 ticker 和 timer。两者都可以实现定时功能,但各自都有自己的使用场景。\nTicker定时器 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func main() { // Ticker 包含一个通道字段C,每隔时间段 d 就向该通道发送当时系统时间。 // 它会调整时间间隔或者丢弃 tick 信息以适应反应慢的接收者。 // 如果d \u0026lt;= 0会触发panic。关闭该 Ticker 可以释放相关资源。 ticker1 := time.NewTicker(5 * time.Second) // 一定要调用Stop(),回收资源 defer ticker1.Stop() go func(t *time.Ticker) { for { // 每5秒中从chan t.C 中读取一次 \u0026lt;-t.C fmt.Println(\u0026#34;Ticker:\u0026#34;, time.Now().Format(\u0026#34;2006-01-02 15:04:05\u0026#34;)) } …"
January 18, 2020
Golang中关于defer语句理解的一道题
"示例 我们先看一下源代码\npackage main import \u0026#34;fmt\u0026#34; func f(n int) (r int) { defer func() { r += n recover() }() var fc func() defer fc() fc = func() { r += 2 } return n + 1 } func main() { fmt.Println(f(3)) } 大家感觉着打印的值是多少呢?5、9还是7?执行完以后发现是7。好像与多数理解的有些出入,为什么是7,而不是9呢。下面我们来分析一下。\n问题分析 对于defer执行的顺序是FIFO这一点都很清楚,我们只需要看搞懂f()函数的执行顺序就行了。\n执行顺序为:\n注册第1个defer 函数, 这里为匿名函数,函数体为 “func() { r += n recover() }()”,内部对应一个函数指针。这里延时函数所有相关的操作一步完成。 注册第2个defer函数,函数名为fc(),无函数体, 函数指针为nil(也有可能指针不会空,但指针指向的内容非函数体类型)。由于只是注册操作还未执行,所以并 …"
January 14, 2020
golang中有关select的几个知识点
"golang中的select语句格式如下\nselect { case \u0026lt;-ch1: // 如果从 ch1 信道成功接收数据,则执行该分支代码 case ch2 \u0026lt;- 1: // 如果成功向 ch2 信道成功发送数据,则执行该分支代码 default: // 如果上面都没有成功,则进入 default 分支处理流程 } 可以看到select的语法结构有点类似于switch,但又有些不同。\nselect里的case后面并不带判断条件,而是一个信道的操作,不同于switch里的case,对于从其它语言转过来的开发者来说有些需要特别注意的地方。\ngolang 的 select 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作每个case语句里必须是一个IO操作,确切的说,应该是一个面向channel的IO操作。\n注:Go 语言的 select 语句借鉴自 Unix 的 select() 函数,在 Unix 中,可以通过调用 select() 函数来监控一系列的文件句柄,一旦其中一个文件句柄发生了 IO 动作,该 select() 调用就会被返回(C 语言中就是这么做的), …"
January 11, 2020
golang中的sync.Pool对象缓存
"参考文章 Golang 的 协程调度机制 与 GOMAXPROCS 性能调优 深入Golang之sync.Pool详解 golang sync.Pool 分析 [译] Go: 理解 Sync.Pool 的设计 视频 sync.pool对象缓存 知识点 Pool只是一个缓存,一个缓存,一个缓存。由于生命周期受GC的影响,一定不要用于数据库连接池这类的应用场景,它只是一个缓存。 golang1.13版本对 Pool 进行了优化,结构体添加了两个字段 victim 和 victimSize。 适应于通过复用,降低复杂对象的创建和GC代价的场景 因为init()的时候会注册一个PoolCleanup函数,他会在gc时清除掉sync.Pool中的所有的缓存的对象。所以每个sync.Pool的生命周期为两次GC中间时段才有效,可以手动进行gc操作 runtime.GC() 由于要保证协程安全,所以会有锁的开销 每个Pool都有一个私有池(协程安全)和共享池(协程不安全),其中私有池只有存放一个值。 每次Get()时会先从当前P的私有池private中获取( 类似MPG模型中的G) 如果获取失败,再 …"
January 11, 2020
golang 的编程模式之“功能选项”
"最近在用go重构iot中的一个服务时,发现库 [email protected] 在初始化消费客户端实现时,实现的极其优雅,代码见 https://github.com/apache/rocketmq-client-go/blob/v2.0.0-rc1/examples/consumer/simple/main.go#L32\nc, _ := rocketmq.NewPushConsumer( consumer.WithGroupName(\u0026#34;testGroup\u0026#34;), consumer.WithNameServer([]string{\u0026#34;127.0.0.1:9876\u0026#34;}), ) err := c.Subscribe(\u0026#34;test\u0026#34;, consumer.MessageSelector{}, func(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) { for i := range msgs { …"
November 8, 2019
Golang中的goroutine泄漏问题
"goroutine作为Go中开发语言中的一大利器,在高并发中发挥着无法忽略的作用。但东西虽好,真正做到用好还是有一些要注意的地方,特别是对于刚刚接触这门开发语言的新手来说,稍有不慎,就极有可能导致goroutine 泄漏。\n什么是goroutine Leak goroutine leak 的意思是go协程泄漏,那么什么又是协程泄漏呢?我们知道每次使用go关键字开启一个gorountine任务,经过一段时间的运行,最终是会结束,从而进行系统资源的释放回收。而如果由于操作不当导致一些goroutine一直处于阻塞状态或者永远运行中,永远也不会结束,这就必定会一直占用系统资源。最球的情况下是随着系统运行,一直在创建此类goroutine,那么最终结果就是程序崩溃或者系统崩溃。这种情况我们一般称为goroutine leak。\n出现的问题 先看一段代码:\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;math/rand\u0026#34; \u0026#34;runtime\u0026#34; \u0026#34;time\u0026#34; ) func query() int { n := …"
July 11, 2019
golang内存对齐(进阶必看)
"先看一个结构体\n// 写法一 type T1 struct { a int8 b int64 c int16 } // 写法二 type T2 struct { a int8 c int16 b int64 } 对于这两个结构体,都有a、b、c三个定义完全一样的字段,只是在定义结构体的时候字段顺序不一样而已,那么两种写法有什么影响吗?\n对于新手来说,感觉着没有什么区别的,只是一个书写顺序不同而已,但对于go编译器来说,则有着很大的区别,特别是在不同架构上(32位/64位)的编译器,在一定程度上对内存的使用大小和执行效率有着一定的不同。这里的主要知识点就是golang语言中的内存对齐概念(alignment guarantee),\n类型的尺寸和结构体字节填充(structure padding) Go白皮书只对以下种类的类型的尺寸进行了明确规定。\n类型种类 尺寸(字节数) ------ ------ byte, uint8, int8 1 uint16, int16 2 uint32, int32, float32 4 uint64, int64 8 float64, complex64 …"
March 23, 2019
goroutine和线程区别
"从调度上看,goroutine的调度开销远远小于线程调度开销。\nOS的线程由OS内核调度,每隔几毫秒,一个硬件时钟中断发到CPU,CPU调用一个调度器内核函数。这个函数暂停当前正在运行的线程,把他的寄存器信息保存到内存中(暂时保存线程状态),查看线程列表并决定接下来运行哪一个线程,再从内存中恢复线程的注册表信息,最后继续执行选中的线程。这种线程切换需要一个完整的上下文切换:即保存一个线程的状态到内存,再恢复另外一个线程的状态,最后更新调度器的数据结构。某种意义上,这种操作还是很慢的。OS 线程调度器\nGo运行的时候包涵一个自己的调度器,这个调度器使用一个称为一个M:N调度技术,m个goroutine到n个os线程(可以用GOMAXPROCS来控制n的数量),Go的调度器不是由硬件时钟来定期触发的,而是由特定的go语言结构来触发的,他不需要切换到内核语境,所以调度一个goroutine比调度一个线程的成本低很多。\n从栈空间上,goroutine的栈空间更加动态灵活。\n每个OS的线程都有一个固定大小的栈内存,通常是2MB,栈内存用于保存在其他函数调用期间哪些正在执行或者临时暂停的函数的局部 …"
October 20, 2018
Golang中struct结构体的的值方法和指针方法
"推荐:Go的方法集详解(360云计算)\n平时我们在写struct的时候,经常会用到一些方法,有些方法是我们熟悉的普通方法,在golang中我们称之为值方法,而另一种则是指针方法。\ntype Person struct { Firstname string Lastname string Age uint8 } // 值方法 func (p Person) show() { fmt.Println(p.Firstname) } // 指针方法 func (p *Person) show2() { fmt.Println(p.Firstname) } 可以看到所谓的值方法与指针方法在编写的时候,只是有无*****号的区别,这个*就是指针的意思。\n那么用法又有何不同呢?\n// 值方法 func (p Person) setFirstName(name string) { p.Firstname = name } // 指针方法 func (p *Person) setFirstName2(name string) { p.Firstname = name } func main() { p …"
October 19, 2018
Golang中的unsafe.Sizeof()简述
"测试环境: 系统 win7 64位 go version: go1.10 windows/amd64\n我们先看一下代码的输出\npackage main import \u0026#34;unsafe\u0026#34; func main() { // string str1 := \u0026#34;abc\u0026#34; println(\u0026#34;string1:\u0026#34;, unsafe.Sizeof(str1)) // 16 str2 := \u0026#34;abcdef\u0026#34; println(\u0026#34;string2:\u0026#34;, unsafe.Sizeof(str2)) // 16 // 数组 arr1 := [...]int{1, 2, 3, 4} println(\u0026#34;array1:\u0026#34;, unsafe.Sizeof(arr1)) // 32 = 8 * 4 arr2 := [...]int{1, 2, 3, 4, 5} println(\u0026#34;array2:\u0026#34;, unsafe.Sizeof(arr2)) // 40 = 8 * 5 // slice 好多人分不清切片和数组的写法区别,其实 …"
October 9, 2018
Golang中的调度器
"golang实现的协程调度器,其实就是在维护一个G、P、M三者间关系的队列。\n介绍(Introduction) ——————— Go 1.1最大的特色之一就是这个新的调度器,由Dmitry Vyukov贡献。新调度器让并行的Go程序获得了一个动态的性能增长,针对它我不能再做点更好的工作了,我觉得我还是为它写点什么吧。\n这篇博客里面大多数东西都已经被包含在了[原始设计文档]( https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw)中了,这个文档的内容相当广泛,但是过于技术化了。\n关于新调度器,你所需要知道的都在那个设计文档中,但是我这篇博客有图片,所以更加清晰易懂。\n带调度器的Go runtime需要什么?(What does the Go runtime need with a scheduler?) ——————————————————————————- 但是在我们开始看新调度器之前,我们需要理解为什么需要调度器。为什么既然操作系统能为我们调度线程了,我们又创造了一个用户空间调度 …"
August 28, 2018
[译]Go里面的unsafe包详解
"unsafe包位置: src/unsafe/unsafe.go\n指针类型: ***类型:**普通指针,用于传递对象地址,不能进行指针运算。 **unsafe.Pointer:**通用指针,用于转换不同类型的指针,不能进行指针运算。 **uintptr:**用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被 GC 回收。\nunsafe.Pointer 可以和 普通指针 进行相互转换。 unsafe.Pointer 可以和 uintptr 进行相互转换。 也就是说 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。\n一般使用流程: 第一步:将结构体 -\u0026gt; 通用指针unsafe.Pointer(struct) -\u0026gt; uintptr(通用指针)获取内存段的起始位置start_pos,并记录下来,第二步使用。 第二步:使用start_pos + unsafe.Offsetof(s.b) -\u0026gt; 将地址转为能用指 …"
August 28, 2018
golang中slice切片理解总结
"首先我们对切片有一个大概的理解,先看一下slice的内部结构,共分三部分,一个是指向底层数组的时候,一个是长度len,另一个就是slice的容量cap了。如cap不足以放在新值的时候,会产生新的内存地址申请。\n先看代码\npackage main import \u0026#34;fmt\u0026#34; func main() { // 创建一个切片,长度为9,容量为10 fmt.Println(\u0026#34;----- 1.测试切片变量append的影响(未申请新的内存空间)-----\u0026#34;) a := make([]int, 9,10) fmt.Printf( \u0026#34;%p len=%d cap=%d %vn\u0026#34; , a, len(a), cap(a), a) // 切片进行append操作,由于原来len(a)长度为9,而cap(a)容量为10,未达到扩展内存的要求,此时新创建的切片变量还指向原来的底层数组,只是数组的后面添加一个新值 // 此时一共两个切片变量,一个是a,另一个是s4。但共指向的一个内存地址 s4 := append(a,4) fmt.Printf(\u0026#34;%p …"
July 2, 2018
Go中复制文件的3种方式
"https://opensource.com/article/18/6/copying-files-go\n更多: https://opensource.com/tags/go"
June 11, 2018
Linux下对进程通信管理的信号机制概述
"今天看到了篇使用golang实现的系统无感重启的文章, https://gravitational.com/blog/golang-ssh-bastion-graceful-restarts/,一般用来平滑处理一些系统服务,避免先停止再启用导致的服务不可用的情况。其中用到了信号机制,这里找了一些文章主要有来介绍这方面的文章,以便加深理解。 https://blog.csdn.net/junyucsdn/article/details/50519248 https://blog.csdn.net/tiany524/article/details/17048069 https://my.oschina.net/chenliang165/blog/125825"
March 8, 2018
Go中slice作为参数传递的一些“坑”
"看明白了这篇文章,下面的例子基本也就明白了\npackage main import \u0026#34;fmt\u0026#34; func main() { a := []int{1,2,3} abc(a) fmt.Println(a) } func abc(a []int) { a[0] = 2 //修改后还是原来的a a = append(a, 4) // 此a非原a,使用append导致了重新分配内存地址(存储空间不足,系统自动分配一块新的足够大的内存地址,此时a的物理内存地址已经发行了变化,并将原来a的值copy一份到新的内存地址,所以这里修改的只是新内存地址的值,原来内存地址的值并没有改变),试着删除这行运行一次再看结果 fmt.Println(a) a[0] = 7 // 新a,因为上面执行了append fmt.Println(a) fmt.Printf(\u0026#34;\\n===\\n\u0026#34;) } 解释: [][1]"
March 3, 2018
golang中string rune byte 三者的关系
"Go 语言中 byte 和 rune 实质上就是 uint8 和 int32 类型。 byte 用来强调数据是 raw data,而不是数字;而 rune 用来表示 Unicode 的 code point。参考 规范.\n在Golang中 string 底层是用byte字节数组存储的,并且是不可以修改的。\nGo语言中的byte和rune区别、对比 例如\ns:=\u0026#34;Go编程\u0026#34; fmt.Println(len(s)) //输出结果应该是8因为中文字符是用3个字节存的(2+3*2=8)。 fmt.Printf(\u0026#34;%d\u0026#34;, len(string(rune(\u0026#39;编\u0026#39;)))) //经测试一个汉字确实占用3个字节,所以结果是3 如果想要获得字符个数的话,需要先转换为rune切片再使用内置的len函数\nfmt.Println(len([]rune(s))) // 结果就是4了。 所以用string存储unicode的话,如果有中文,按下标是访问不到的,因为你只能得到一个byte。 要想访问中文的话,还是要用rune切片,这样就能按下表访问。\n总结: rune …"
April 3, 2016
[翻译]理解 GO 语言的内存使用
"许多人在刚开始接触 Go 语言时,经常会有的疑惑就是“为什么一个 Hello world 会占用如此之多的内存?”。 Understanding Go Lang Memory Usage 很好的解释了这个问题。不过“简介”就是“简介”,更加深入的内容恐怕要读者自己去探索了。另外,文章写到最后,作者飘了,估计引起了一些公愤,于是又自己给自己补刀,左一刀,右一刀……\n————翻译分隔线————\n理解 Go 语言的内存使用 2014年12月22日,星期一\n温馨提示:这仅是关于 Go 语言内存的简介,俗话说不入虎穴、焉得虎子,读者可以进行更加深入的探索。\n大多数 Go 开发者都会尝试像这样简单的 hello world 程序:\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func main() { fmt.Println(\u0026#34;hi\u0026#34;) time.Sleep(30 * time.Second) } 然后他们就完全崩溃了。\n这个笔记本也只有 16 G 内存!\n虚拟内存 vs 常驻内存 Go 管理内存的方式可能与你以前使用的 …"
March 25, 2016
Profiling Go Programs
"转自: http://blog.golang.org/profiling-go-programs (需翻墙)\nThe Go Blog Profiling Go Programs 24 June 2011\nAt Scala Days 2011, Robert Hundt presented a paper titled Loop Recognition in C++/Java/Go/Scala. 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 …"
March 24, 2016
golang中并发实例
"package main import ( \u0026#34;fmt\u0026#34; //\u0026#34;runtime\u0026#34; \u0026#34;os\u0026#34; \u0026#34;runtime/pprof\u0026#34; // 引用pprof package \u0026#34;time\u0026#34; ) func main() { f, _ := os.Create(\u0026#34;profile_file\u0026#34;) pprof.StartCPUProfile(f) // 开始cpu profile,结果写到文件f中 defer pprof.StopCPUProfile() // 结束profile startTime := time.Now().Second() //runtime.GOMAXPROCS(runtime.NumCPU()) // 注意这里的缓存大小 ch := make(chan int, 100) quit := make(chan bool) // 注意这里把读取chan操作放在了写入chan之前了(为了安全建议对chan的goroutines读取放在前面,写入放在后面) // 如果把这行放在了for逻辑后面,则上 …"
March 24, 2016
在Golang中使用json
"由于要开发一个小型的web应用,而web应用大部分都会使用json作为数据传输的格式,所以有了这篇文章。\n包引用 import ( \u0026ldquo;encoding/json\u0026rdquo; \u0026ldquo;github.com/bitly/go-simplejson\u0026rdquo; // for json get )\n用于存放数据的结构体 type MyData struct { Name string json:\u0026quot;item\u0026quot; Other float32 json:\u0026quot;amount\u0026quot; }\n这里需要注意的就是后面单引号中的内容。\njson:\u0026quot;item\u0026quot;\n这个的作用,就是Name字段在从结构体实例编码到JSON数据格式的时候,使用item作为名字。算是一种重命名的方式吧。\n编码JSON var detail MyData\ndetail.Name = \u0026ldquo;1\u0026rdquo; detail.Other = \u0026ldquo;2\u0026rdquo;\nbody, err := json.Marshal(detail) if err != nil { …"
March 24, 2016
golang的json操作
"package main import ( \u0026#34;encoding/json\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; ) type ConfigStruct struct { Host string `json:\u0026#34;host\u0026#34;` Port int `json:\u0026#34;port\u0026#34;` AnalyticsFile string `json:\u0026#34;analytics_file\u0026#34;` StaticFileVersion int `json:\u0026#34;static_file_version\u0026#34;` StaticDir string `json:\u0026#34;static_dir\u0026#34;` TemplatesDir string `json:\u0026#34;templates_dir\u0026#34;` SerTcpSocketHost string `json:\u0026#34;serTcpSocketHost\u0026#34;` SerTcpSocketPort int `json:\u0026#34;serTcpSocketPort\u0026#34;` Fruits …"
March 23, 2016
golang中的md5的用法
"代码\npackage main import ( \u0026#34;crypto/md5\u0026#34; \u0026#34;encoding/hex\u0026#34; \u0026#34;fmt\u0026#34; ) func main() { // md5 加密的第一种方法 srcData := []byte(\u0026#34;iyannik0215\u0026#34;) cipherText1 := md5.Sum(srcData) fmt.Printf(\u0026#34;md5 encrypto is \u0026#34;iyannik0215\u0026#34;: %x n\u0026#34;, cipherText1) // md5 加密的第二种方法 hash := md5.New() hash.Write(srcData) cipherText2 := hash.Sum(nil) hexText := make([]byte, 32) hex.Encode(hexText, cipherText2) fmt.Println(\u0026#34;md5 encrypto is \u0026#34;iyannik0215\u0026#34;:\u0026#34;, string(hexText)) } # …"
June 15, 2015
golang中chan实例
"package main import \u0026#34;fmt\u0026#34; func main() { data := make(chan int) // 数据交换队列 exit := make(chan bool) // 退出通知 go func() { for d := range data { // 从队列迭代接收数据,直到 close 。 fmt.Println(d) } fmt.Println(\u0026#34;recv over.\u0026#34;) exit \u0026lt;- true // 发出退出通知。 }() data \u0026lt;- 1 // 发送数据。 data \u0026lt;- 2 data \u0026lt;- 3 close(data) // 关闭队列。 fmt.Println(\u0026#34;send over.\u0026#34;) \u0026lt;-exit // 等待退出通知。 } 输出结果:\n1 2 3 send over. recv over. 而如果将上面与 exit chan有关的三行删除掉,则结果为:"
June 13, 2015
golang中chan的理解与使用教程
"对于 chan 介绍见: https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/02.7.md\n这里我们主要通过实例来介绍对chan的理解及用法.\n无Buffer的Channels 实例1:\nfunc main() { ci := make(chan int) ci \u0026lt;- 4 value := \u0026lt;-ci fmt.Println(value) } 执行结果错误为:\nfatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]:\n从上面“fatal error: all goroutines are asleep - deadlock!” 这句我们可以看出是groutings 阻塞了,这里为写阻塞,从“goroutine 1 [chan send]”可以看出来。\n这一点文档里已经说明阻塞的原因了:\n默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使 …"
June 9, 2015
golang中flag包的用法
"golang中flag包主要用来CLI下,获取命令参数,示例如下mysql.go:\npackage main import ( \u0026#34;flag\u0026#34; \u0026#34;fmt\u0026#34; ) func main() { host := flag.String(\u0026#34;h\u0026#34;, \u0026#34;localhost\u0026#34;, \u0026#34;请指定一个主机\u0026#34;) user := flag.String(\u0026#34;u\u0026#34;, \u0026#34;root\u0026#34;, \u0026#34;请指定数据库用户\u0026#34;) 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;) //var name string //flag.StringVar(\u0026amp;name, \u0026#34;u\u0026#34;, \u0026#34;root\u0026#34;, \u0026#34;请指定用户名\u0026#34;) flag.Parse() //参数解析 fmt.Println(\u0026#34;主机地 …"
April 8, 2015
Golang语言的GOPATH与工作目录详解
"这篇文章主要介绍了Go语言的GOPATH与工作目录详解,本文详细讲解了GOPATH设置、应用目录结构、编译应用等内容,需要的朋友可以参考下\nGOPATH设置\ngo 命令依赖一个重要的环境变量:$GOPATH\n(注:这个不是Go安装目录( GOROOT)。下面以笔者的工作目录为说明,请替换自己机器上的工作目录。)\n在类似 Unix 环境大概这样设置:\nexport GOPATH=/home/apple/mygo 为了方便,应该把新建以上文件夹,并且把以上一行加入到 .bashrc 或者 .zshrc 或者自己的 sh 的配置文件中。\nWindows 设置如下,新建一个环境变量名称叫做GOPATH:\nGOPATH=c:mygo GOPATH允许多个目录,当有多个目录时,请注意分隔符,多个目录的时候Windows是分号,Linux系统是冒号,当有多个GOPATH时,默认会将go get的内容放在第一个目录下\n以上 $GOPATH 目录约定有三个子目录:\n1.src 存放源代码(比如:.go .c .h .s等)\n2.pkg 编译后生成的文件(比如:.a)\n3.bin 编译后生成的可执行文件( …"
June 20, 2014
golint—golang代码质量检测
"github: https://github.com/golang/lint\ngolint是类似javascript中的jslint的工具,主要功能就是检测代码中不规范的地方。golint用于检测go代码。\n使用 $ go get github.com/golang/lint $ go install github.com/golang/lint golint 文件名或者目录 检测对应的代码。\ngolint会输出一些代码存在的问题: 比如:\nrecorder.go:55:5: exported var RecordBind should have comment or be unexported recorder.go:158:1: exported function Record_ErrorRecord should have comment or be unexported recorder.go:173:6: don\u0026#39;t use underscores in Go names; type Data_MemStats should be DataMemStats …"
March 8, 2014
golang中for循环方法的差异
"用for循环遍历字符串时,也有 byte 和 rune 两种方式.第一种试为byte,第二种rune。\ngolang中string rune byte 三者的关系 https://blog.haohtml.com/archives/17646\npackage main import ( \u0026#34;fmt\u0026#34; ) func main() { s := \u0026#34;abc汉字\u0026#34; for i := 0; i \u0026lt; len(s); i++ { fmt.Printf(\u0026#34;%c,\u0026#34;, s[i]) } fmt.Println() for _, r := range s { fmt.Printf(\u0026#34;%cn\u0026#34;, r) } } 输出结果:\na,b,c,æ,±,,å,,, a b c 汉 字 "
October 31, 2013
Golang import使用说明
"我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:\nimport( \u0026#34;fmt\u0026#34; ) 然后我们代码里面可以通过如下的方式调用\nfmt.Println(\u0026#34;hello world\u0026#34;) 上面这个fmt是Go语言的标准库,他其实是去goroot下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:\n1.相对路径\nimport “./model” //当前文件同一目录的model目录,但是不建议这种方式来import 2.绝对路径\nimport “shorturl/model” //加载gopath/src/shorturl/model模块\n上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事\n1.点操作\n我们有时候会看到如下的方式导入包\nimport( . \u0026#34;fmt\u0026#34; ) 这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println(“hello …"
October 22, 2013
golang实现验证电子信箱和手机格式合法性(正则表达式)
"邮箱验证\nfunc checkEmail(email string) (b bool) { if m, _ := regexp.MatchString(\u0026#34;^([a-zA-Z0-9\\_-])+@([a-zA-Z0-9\\_-])+(.[a-zA-Z0-9_-])+\u0026#34;, email); !m { return false } return true } 手机验证\npackage main import ( \u0026#34;regexp\u0026#34; ) const ( regular = \u0026#34;^(13[0-9]|14[57]|15[0-35-9]|18[07-9])\\\\d{8}$\u0026#34; ) func validate(mobileNum string) bool { reg := regexp.MustCompile(regular) return reg.MatchString(mobileNum) } func main() { if validate(\u0026#34;13888888888\u0026#34;) { println(\u0026#34;是手机号\u0026#34;) return } …"
October 22, 2013
[必读]golang语言报错信息fatal error: all goroutines are asleep – deadlock!
"出错信息fatal error: all goroutines are asleep – deadlock! 出错信息的意思是: 在main goroutine线,期望从管道中获得一个数据,而这个数据必须是其他goroutine线放入管道的 但是其他goroutine线都已经执行完了(all goroutines are asleep),那么就永远不会有数据放入管道。 所以,main goroutine线在等一个永远不会来的数据,那整个程序就永远等下去了。 这显然是没有结果的,所以这个程序就说“算了吧,不坚持了,我自己自杀掉,报一个错给代码作者,我被deadlock了” 例子:\npackage main func main() { c := make(chan bool) go func() { c \u0026lt;- true }() \u0026lt;-c //这里从c管道,取到一个true \u0026lt;-c //这行导致deadlock,因为这时的c管道,永远都取不到数据(注释掉这行就不报错) } "
October 22, 2013
golang日志模块测试
"package main import ( \u0026#34;fmt\u0026#34; \u0026#34;log\u0026#34; \u0026#34;os\u0026#34; ) func main(){ logfile,err := os.OpenFile(\u0026#34;d:/workspace/golang/test.log\u0026#34;,os.O\\_RDWR|os.O\\_CREATE|os.O_APPEND,0); if err!=nil { fmt.Printf(\u0026#34;%s\\r\\n\u0026#34;,err.Error()); os.Exit(-1); } defer logfile.Close(); logger := log.New(logfile,\u0026#34;\\r\\n\u0026#34;,log.Ldate|log.Ltime|log.Llongfile); logger.Println(\u0026#34;hello\u0026#34;); logger.Println(\u0026#34;oh….\u0026#34;); logger.Fatal(\u0026#34;test\u0026#34;); logger.Fatal(\u0026#34;test2\u0026#34;); } 这里打开日志文件的时候,同时使用 …"
October 7, 2013
[翻译]绝妙的 channel
"在编写 golang 程序的过程中,channel 会经常使用。本文对 channel 的使用的确很特别,同时也非常实用。\n原文在此: http://dave.cheney.net/2013/04/30/curious-channels\n在编写 golang 程序的过程中,channel 会经常使用。本文对 channel 的使用的确很特别,同时也非常实用。\n原文在此:http://dave.cheney.net/2013/04/30/curious-channels\n翻译:http://mikespook.com/2013/05/%E7%BF%BB%E8%AF%91%E7%BB%9D%E5%A6%99%E7%9A%84-channel/#more-1635"
October 4, 2013
VIM编辑器下go语法高亮显示
"Go in Vim The standard Go distribution includes a Go syntax file for Vim in go/misc/vim/.\nInstallation Instructions Place $GOROOT/misc/vim/syntax/go.vim in ~/.vim/syntax/ and put the following in ~/.vim/ftdetect/go.vim:\n在go的安装目录里有/misc/vim/syntax 他 /misc/vim/ftdetect 两个目录,将里面的文件复制到~/.vim/相应的目录里即可。\nau BufRead,BufNewFile *.go set filetype=go\nExtras and Alternative Files An alternative indent file for Vim by Alecs King can be found here.\nAutocompletion The gocode daemon by nsf includes a vim script …"
October 2, 2013
gozmq的安装与使用教程(zeromq分布式消息队列+golang)
"实现功能:用go实现消息队列的写入与读取(打算用在发送邮件服务)\n环境工具: Centos 64X 6.4 zeromq 3.2.4: zeromq.org golang: http://golang.org/\n一.安装golang( http://golang.org/doc/install) 这一步很简单,只需要从 http://code.google.com/p/go/downloads 下载到服务器,解压到/usr/local/go目录,再设置一下系统变量就可以了.\nwget https://go.googlecode.com/files/go1.1.2.linux-amd64.tar.gz tar -C /usr/local -xzf go1.1.2.linux-amd64.tar.gz 设置系统变量GOROOT\nAdd /usr/local/go/bin to the PATH environment variable. You can do this by adding this line to your /etc/profile (for a system-wide …"
August 12, 2013
golang中结构体的初始化方法的不同用法(new方法)
"自定义一个结构体\ntype Rect struct { x, y float64 width, height float64 } 初始化方法:\nrect1 := new(Rect) rect2 := \u0026amp;Rect{} rect3 := \u0026amp;Rect{0, 0, 100, 200} rect4 := \u0026amp;Rect{width:100, height:200} 注意这几个变量全部为指向Rect结构的指针(指针变量),因为使用了new()函数和\u0026amp;操作符。\n而如果使用方法\na := Rect{} 则表示这个是一个Rect{}结构类型.两者是不一样的.参考代码:\nfunc main() { a := Rect{} a.x = 15 rect1 := \u0026amp;Rect{0, 0, 100, 200} rect1.x = 10 fmt.Printf(\u0026#34;%v\\n%T\\n\u0026#34;, a, a) fmt.Printf(\u0026#34;%v\\n%T\\n\u0026#34;, rect1, rect1) } 运行结果为:\n{15 0 0 0} main.Rect \u0026amp;{10 0 …"
August 11, 2013
golang中的文档管理
"foo.go\n// CopyRight 2013 The Go Author. All Right reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE fifle. /* Package foo implements a set of simple mathematical functions. These comments are for demonstration purpose only. Nothing more. If you have any questions,please don’t hesitate to add yourself to [email protected]. you can alse visit golang.org for full Go documentation."
August 11, 2013
golang中的map数据类型操作实例
"package main import ( \u0026#34;fmt\u0026#34; ) type stu struct { Name string Age int } func main() { // 声明一个map变量student,键名为string,值为stu var student map[string]stu // 给map变量创建值,同时指定最多可以存储5个stu值 student = make(map[string]stu, 5) // map元素赋值 student[\u0026#34;stu1\u0026#34;] = stu{\u0026#34;zhao\u0026#34;, 25} student[\u0026#34;stu2\u0026#34;] = stu{\u0026#34;zhang\u0026#34;, 28} student[\u0026#34;stu3\u0026#34;] = stu{\u0026#34;sun\u0026#34;, 32} student[\u0026#34;stu4\u0026#34;] = stu{\u0026#34;li\u0026#34;, 40} student[\u0026#34;stu5\u0026#34;] = stu{} //上面方式的简写方法 /* student := …"
August 9, 2013
go语言单元测试
"Go本身提供了一套轻量级的测试框架.符合规则的测试代码会在运行测试时被自动识别并执行.单元测试源文件的命名规则如平衡点:在需要测试的包下面创建以”_test”结尾的go文件,开如[^.]*_test.go\nGo单元测试函数分为两在类.功能测试函数和性能测试函数,分别以Test和Benchmark为函数名前缀并以*testing.T 和 *testing.B 为单一参数的函数。\nfunc TestAdd1(t *testing.T) func BenchmarkAdd1(t *testing.T) 测试工具会根据函数中的实际执行动作得到不同的测试结果。\n功能测试函数会根据测试代码执行过程中是否发生错误来反馈结果; 性能测试函数仅仅打印出来测试所花费时间,用来判断程序性能;\n准备 新建一个文件,命名为 go_test.go\npackage go_test import \u0026#34;testing\u0026#34; func Add(a, b int) int { return a + b } 功能测试 在go_test.go文件里添加以下代码\nfunc TestAdd1(t *testing.T) …"
July 23, 2013
用golang发送邮件
"配置文件 conf.json\n{ \u0026#34;Username\u0026#34;: \u0026#34;[email protected]\u0026#34;, \u0026#34;Password\u0026#34;: \u0026#34;123456\u0026#34;, \u0026#34;Smtphost\u0026#34;:\u0026#34;smtp.163.com:25\u0026#34; } 主程序 sendmail.go\npackage main import ( \u0026#34;encoding/json\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;io\u0026#34; \u0026#34;log\u0026#34; \u0026#34;net/smtp\u0026#34; \u0026#34;os\u0026#34; \u0026#34;strings\u0026#34; ) type cfgmail struct { Username string Password string Smtphost string } type cfg struct { Name, Text string } func main() { // 从json文件中读取发送邮件服务器配置信息 cfgjson := getConf() var cfg cfgmail dec := …"
June 18, 2013
测试golang中的多核多线程
"“并发 (concurrency)” 和 “并行 ( parallelism)” 是不同的。在单个 CPU 核上,线程通过时间片或者让出控制权来实现任务切换,达到 “同时” 运行多个任务的⺫的,这就是所谓的并发。但实际上任何时刻都只有一个任务被执行,其他任务通过某种算法来排队。\n多核 CPU 可以让同个进程内的 “多个线程” 做到真正意义上的同时运,它们之间不需要排队 (依然会发生排队,因为线程数量可能超出 CPU 核数量,还有其他的进程等等。这里说的是一个理想状况),这才是并行。除了多核,并行计算还可能是多台机器上部署运行。\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;runtime\u0026#34; ) func test(c chan bool, n int) { x := 0 for i := 0; i \u0026lt; 1000000000; i++ { x += i } println(n, x) if n == 9 { c \u0026lt;- true } } func main() { runtime.GOMAXPROCS(1) //设置cpu的核的数量, …"
June 17, 2013
golang中的Array 、Slices 和 Maps
"**注意slice和数组在声明时的区别:**声明数组时,方括号内写明了数组的长度或使用...自动计算长度,而声明slice时,方括号内没有任何字符。\narr1 := [10]int{1,2,3,4} //数组,长度为10,只有4个元素指定,其它的元素值默认为0 arr2 := [...]string{\u0026#34;a\u0026#34;,\u0026#34;b\u0026#34;,\u0026#34;c\u0026#34;} //数组,长度自适应,这里长度为3 s1 := []int{1,2,3,4} //slice,目前长度为4,可能通过append来动态添加元素个数 示例:\npackage main import ( \u0026#34;fmt\u0026#34; ) func main() { //array example arr := [10]int{1, 2, 3} //array 指定前三个值,其它值使用默认类型值0 fmt.Println(len(arr)) fmt.Println(arr) //a1 := append(arr, 4, 5) //数组不支持append,只有slice才支持append //fmt.Println(a1) …"
June 4, 2013
golang中包的用法
"将d:/gotest/ 目录加入到GOPATH中.这里会涉及到包和结构体还有一些方法的用法,可以再深入的了了解一下\n注意一下一些struct和 func 名称的大小写问题.\n首先要在 $GOPATH/src 目录里创建一个包名目录,这里包名目录为stu,与文件名一样(也可以不一样),大概流程参考: d:/gotest/src/main/main.go\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;stu\u0026#34; ) func main() { //sxf := new(stu.Stu) sxf := \u0026amp;stu.Stu{} sxf.SetName(\u0026#34;zhangli\u0026#34;) a := sxf.GetName() fmt.Println(a) } d:/gotest/src/stu/stu.go\npackage stu type Stu struct { name string //age int } func (s *Stu) SetName(name string) { s.name = name } func (s *Stu) …"
April 23, 2013
golang中实现自定义数据类型struct
"可以参考: golang中的函数\nfunc.go\npackage main import ( \u0026#34;fmt\u0026#34; ) type stu struct { Name string //首字母大写,允许其它包直接使用,可以直接使用 stu.Name = \u0026#39;test\u0026#39; 也可以使用 setName和getName age int //不允许外面的包使用,可以使用 setAge和getAge方法 } func main() { perl := new(stu) perl.Name = \u0026#34;zhang\u0026#34; // age setAge(perl, 30) age := getAge(perl) fmt.Printf(\u0026#34;%v\\n\u0026#34;, age) //name var name string perl.setName(\u0026#34;sun\u0026#34;) name = perl.getName() fmt.Printf(\u0026#34;%i\\n\u0026#34;, name) //print struct fmt.Printf(\u0026#34;%v\\n\u0026#34;, perl) } …"
December 31, 2012
golang中的函数
"函数是构建Go程序的基础部件;所遇有趣的事情都是在它其中发生的。函数 的定义看起来像这样: Listing 3.1. 函数定义\ntype mytype int 新的类型,参阅第 5 章 0 保留字func用于定义一个函数;\n1 函数可以定义用于特定的类型,这类函数更加通俗的称呼是method。这 部分称作receiver而它是可选的(可参考: http://blog.haohtml.com/archives/13766)。如下图:\n2 funcname是你函数的名字; 3 int类型的变量q作为输入参数。参数用pass-by-value方式传递,意味着它 们会被复制; 4 变量r和s是这个函数的命名返回值。在Go的函数中可以返回多个值。 参阅第32页的“多值返回”。如果不想对返回的参数命名,只需要提供类 型:(int,int)。如果只有一个返回值,可以省略圆括号。如果函数是一 个子过程,并且没有任何返回值,也可以省略这些内容; 5 这是函数体,注意return是一个语句,所以包裹参数的括号是可选的。\n这里有两个例子,左边的函数没有返回值,右边的只是简单的将输入返回。 …"
December 27, 2012
[golang]将函数作为值
"就像其它在Go中的几乎所有的东西,函数也同样是值而已.它们可以像下面这样赋值给变量:\npackage main import \u0026#34;fmt\u0026#34; func main() { f := func() { fmt.Println(\u0026#34;func\u0026#34;) } // 下面才开始调用函数 f() } 结果会打印出 func 字符串。\n另一种用法是立即调用函数,但是要求匿名函数要有返回值才可以,不然会提示错误信息."
December 15, 2012
windows 下搭建 GoLang 语言开发环境
"golang官方二进制分发包包括FreeBSD, Linux, Mac OS X (Snow Leopard/Lion), and Windows等平台,包括32位、64位等版本。\n我自己使用的是windows 32位分发包,MSI格式的,下载地址为: http://code.google.com/p/go/downloads/list\ngolang支持交叉编译,也就是说你在32位平台的机器上开发,可以编译生成64位平台上的可执行程序。\n环境变量说明: $GOROOT 指向golang安装之后的根目录,windows平台下默认为c:\\go,会在安装过程中由安装程序自动写入系统环境变量。 $GOARCH 目标平台(编译后的目标平台)的处理器架构(386、amd64、arm) $GOOS 目标平台(编译后的目标平台)的操作系统(darwin、freebsd、linux、windows) $GOBIN 指向安装之后根目录下的bin目录,即$GOROOT/bin,windows平台下默认为c:\\go\\bin,会在安装过程中由安装程序自动添加到PATH变量中\n配置windows环境变量: …"