工作,学习,生活,这里将会有一些记录. 备用域名:http://meisw.wdlinux.cn 注册 | 登陆
浏览模式: 标准 | 列表分类:golang

go linux 安装

 wget -c http://golangtc.com/static/go/1.7.4/go1.7.4.linux-amd64.tar.gz

tar zxvf go1.7.4.linux-amd64.tar.gz
 
vi .bashrc
export GOROOT=/root/go
export PATH=$GOROOT/bin:$PATH
export GOPATH=/root/go/src
 
 
yum install -y mercurial
yum install -y git

golang之cond锁定期唤醒锁

 cond的主要作用就是获取锁之后,wait()方法会等待一个通知,来进行下一步锁释放等操作,以此控制锁合适释放,释放频率。
package main

import (
        "fmt"
        "sync"
        "time"
)
var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)

func test(x int) {
        cond.L.Lock() //获取锁
        cond.Wait()//等待通知 暂时阻塞
        fmt.Println(x)
        time.Sleep(time.Second * 1)
        cond.L.Unlock()//释放锁
}
func main() {
        for i := 0; i < 40; i++ {
                go test(i)
        }
        fmt.Println("start all")
        time.Sleep(time.Second * 3)
        fmt.Println("broadcast")
        cond.Signal() // 下发一个通知给已经获取锁的goroutine
        time.Sleep(time.Second * 3)
        cond.Signal()// 3秒之后 下发一个通知给已经获取锁的goroutine
        time.Sleep(time.Second * 3)
        cond.Broadcast()//3秒之后 下发广播给所有等待的goroutine
        time.Sleep(time.Second * 60)
}

匿名无限循环

 // Creates an infinite stream of integers counting up from 1

func createCounter() <-chan int {
    result := make(chan int)
    go func() {
        var n int
        for {
            n++
            result <- n
        }
    }()
    return result
}

理解 Go Context 机制

 

什么是Context

最近在公司分析gRPC源码,proto文件生成的代码,接口函数第一个参数统一是 ctx context.Context 接口,公司不少同事都不了解这样设计的出发点是什么,其实我也不了解其背后的原理。今天趁着 妮妲 台风妹子正面登陆深圳,全市停工、停课、停业,在家休息找了一些资料研究把玩一把。

Context 通常被译作 上下文 ,它是一个比较抽象的概念。在公司技术讨论时也经常会提到 上下文 。一般理解为程序单元的一个运行状态、现场、快照,而翻译中 上下又很好地诠释了其本质,上下上下则是存在上下层的传递,  会把内容传递给  。在Go语言中,程序单元也就指的是Goroutine。

每个Goroutine在执行之前,都要先知道程序当前的执行状态,通常将这些执行状态封装在一个 Context 变量中,传递给要执行的Goroutine中。上下文则几乎已经成为传递与请求同生存周期变量的标准方法。在网络编程下,当接收到一个网络请求Request,处理Request时,我们可能需要开启不同的Goroutine来获取数据与逻辑处理,即一个请求Request,会在多个Goroutine中处理。而这些Goroutine可能需要共享Request的一些信息;同时当Request被取消或者超时的时候,所有从这个Request创建的所有Goroutine也应该被结束。

context包

Go的设计者早考虑多个Goroutine共享数据,以及多Goroutine管理机制。 Context介绍请参考 Go Concurrency Patterns: Context , golang.org/x/net/context 包就是这种机制的实现。

context 包不仅实现了在程序单元之间共享状态变量的方法,同时能通过简单的方法,使我们在被调用程序单元的外部,通过设置ctx变量值,将过期或撤销这些信号传递给被调用的程序单元。在网络编程中,若存在A调用B的API, B再调用C的API,若A调用B取消,那也要取消B调用C,通过在A,B,C的API调用之间传递 Context ,以及判断其状态,就能解决此问题,这是为什么gRPC的接口中带上 ctx context.Context 参数的原因之一。

Go1.7(当前是RC2版本)已将原来的 golang.org/x/net/context 包挪入了标准库中,放在$GOROOT/src/context下面。标准库中 net 、 net/http 、 os/exec 都用到了 context 。同时为了考虑兼容,在原 golang.org/x/net/context 包下存在两个文件, go17.go 是调用标准库的 context 包,而 pre_go17.go 则是之前的默认实现,其介绍请参考 go程序包源码解读 。

context 包的核心就是 Context 接口,其定义如下:

type Context interface {     Deadline() (deadline time.Time, ok bool)     Done() <-chan struct{}     Err() error     Value(key interface{}) interface{} }
  • Deadline 会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。

  • Done 方法返回一个信道(channel),当 Context 被撤销或过期时,该信道是关闭的,即它是一个表示Context是否已关闭的信号。

  • 当 Done 信道关闭后, Err 方法表明 Contex t被撤的原因。

  • Value 可以让Goroutine共享一些数据,当然获得数据是协程安全的。但使用这些数据的时候要注意同步,比如返回了一个map,而这个map的读写则要加锁。

Context 接口没有提供方法来设置其值和过期时间,也没有提供方法直接将其自身撤销。也就是说, Context 不能改变和撤销其自身。那么该怎么通过 Context 传递改变后的状态呢?

context使用

无论是Goroutine,他们的创建和调用关系总是像层层调用进行的,就像人的辈分一样,而更靠顶部的Goroutine应有办法主动关闭其下属的Goroutine的执行(不然程序可能就失控了)。为了实现这种关系,Context结构也应该像一棵树,叶子节点须总是由根节点衍生出来的。

要创建Context树,第一步就是要得到根节点, context.Background 函数的返回值就是根节点:

func Background() Context

该函数返回空的Context,该Context一般由接收请求的第一个Goroutine创建,是与进入请求对应的Context根节点,它不能被取消、没有值、也没有过期时间。它常常作为处理Request的顶层context存在。

有了根节点,又该怎么创建其它的子节点,孙节点呢?context包为我们提供了多个函数来创建他们:

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithValue(parent Context, key interface{}, val interface{}) Context

函数都接收一个 Context 类型的参数 parent ,并返回一个 Context 类型的值,这样就层层创建出不同的节点。子节点是从复制父节点得到的,并且根据接收参数设定子节点的一些状态值,接着就可以将子节点传递给下层的Goroutine了。

再回到之前的问题:该怎么通过 Context 传递改变后的状态呢?使用 Context 的Goroutine无法取消某个操作,其实这也是符合常理的,因为这些Goroutine是被某个父Goroutine创建的,而理应只有父Goroutine可以取消操作。在父Goroutine中可以通过WithCancel方法获得一个cancel方法,从而获得cancel的权利。

第一个 WithCancel 函数,它是将父节点复制到子节点,并且还返回一个额外的 CancelFunc 函数类型变量,该函数类型的定义为:

type CancelFunc func()

调用 CancelFunc 对象将撤销对应的 Context 对象,这就是主动撤销 Context 的方法。在父节点的 Context 所对应的环境中,通过 WithCancel 函数不仅可创建子节点的 Context ,同时也获得了该节点 Context 的控制权,一旦执行该函数,则该节点 Context 就结束了,则子节点需要类似如下代码来判断是否已结束,并退出该Goroutine:

select {     case <-cxt.Done():         // do some clean... }

WithDeadline 函数的作用也差不多,它返回的Context类型值同样是 parent 的副本,但其过期时间由 deadline 和 parent 的过期时间共同决定。当 parent 的过期时间早于传入的 deadline 时间时,返回的过期时间应与 parent 相同。父节点过期时,其所有的子孙节点必须同时关闭;反之,返回的父节点的过期时间则为 deadline 。

WithTimeout 函数与 WithDeadline 类似,只不过它传入的是从现在开始Context剩余的生命时长。他们都同样也都返回了所创建的子Context的控制权,一个 CancelFunc 类型的函数变量。

当顶层的Request请求函数结束后,我们就可以cancel掉某个context,从而层层Goroutine根据判断 cxt.Done() 来结束。

WithValue 函数,它返回 parent 的一个副本,调用该副本的Value(key)方法将得到val。这样我们不光将根节点原有的值保留了,还在子孙节点中加入了新的值,注意若存在Key相同,则会被覆盖。

小结

context 包通过构建树型关系的Context,来达到上一层Goroutine能对传递给下一层Goroutine的控制。对于处理一个Request请求操作,需要采用 context 来层层控制Goroutine,以及传递一些变量来共享。

  • Context对象的生存周期一般仅为一个请求的处理周期。即针对一个请求创建一个Context变量(它为Context树结构的根);在请求处理结束后,撤销此ctx变量,释放资源。

  • 每次创建一个Goroutine,要么将原有的Context传递给Goroutine,要么创建一个子Context并传递给Goroutine。

  • Context能灵活地存储不同类型、不同数目的值,并且使多个Goroutine安全地读写其中的值。

  • 当通过父Context对象创建子Context对象时,可同时获得子Context的一个撤销函数,这样父Context对象的创建环境就获得了对子Context将要被传递到的Goroutine的撤销权。

  • 在子Context被传递到的goroutine中,应该对该子Context的Done信道(channel)进行监控,一旦该信道被关闭(即上层运行环境撤销了本goroutine的执行),应主动终止对当前请求信息的处理,释放资源并返回。

使用原则

Programs that use Contexts should follow these rules to keep interfaces consistent across packages and enable static analysis tools to check context propagation:

使用Context的程序包需要遵循如下的原则来满足接口的一致性以及便于静态分析。

  • Do not store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it. The Context should be the first parameter, typically named ctx;不要把Context存在一个结构体当中,显式地传入函数。Context变量需要作为第一个参数使用,一般命名为ctx;

  • Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use;即使方法允许,也不要传入一个nil的Context,如果你不确定你要用什么Context的时候传一个context.TODO;

  • Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions;使用context的Value相关方法只应该用于在程序和接口中传递的和请求相关的元数据,不要用它来传递一些可选的参数;

  • The same Context may be passed to functions running in different goroutines; Contexts are safe for simultaneous use by multiple goroutines;同样的Context可以用来传递到不同的goroutine中,Context在多个goroutine中是安全的;

参考:

[1] https://blog.golang.org/context

[2] http://blog.golang.org/pipelines

[3] http://studygolang.com/articles/5131

[4] http://blog.csdn.net/sryan/article/details/51969129

[5] https://peter.bourgon.org/blog/2016/07/11/context.html

[6] http://www.tuicool.com/articles/vaieAbQ

golang中context包解读

简介

golang 中的创建一个新的 goroutine , 并不会返回像c语言类似的pid,所有我们不能从外部杀死某个goroutine,所有我就得让它自己结束,之前我们用 channel + select 的方式,来解决这个问题,但是有些场景实现起来比较麻烦,例如由一个请求衍生出的各个 goroutine 之间需要满足一定的约束关系,以实现一些诸如有效期,中止routine树,传递请求全局变量之类的功能。于是google 就为我们提供一个解决方案,开源了 context 包。使用 context 实现上下文功能约定需要在你的方法的传入参数的第一个传入一个 context.Context 类型的变量。

源码剖析

context.Context 接口

context 包的核心

//  context 包里的方法是线程安全的,可以被多个 goroutine 使用    

type Context interface {               

    // 当Context 被 canceled 或是 times out 的时候,Done 返回一个被 closed 的channel      

    Done() <-chan struct{}        


    // 在 Done 的 channel被closed 后, Err 代表被关闭的原因   

    Err() error 


    // 如果存在,Deadline 返回Context将要关闭的时间  

    Deadline() (deadline time.Time, ok bool)


    // 如果存在,Value 返回与 key 相关了的值,不存在返回 nil  

    Value(key interface{}) interface{}

}

我们不需要手动实现这个接口,context 包已经给我们提供了两个,一个是 Background(),一个是 TODO(),这两个函数都会返回一个 Context 的实例。只是返回的这两个实例都是空 Context。

主要结构

cancelCtx 结构体继承了 Context ,实现了 canceler 方法:

//*cancelCtx 和 *timerCtx 都实现了canceler接口,实现该接口的类型都可以被直接canceled

type canceler interface {

    cancel(removeFromParent bool, err error)

    Done() <-chan struct{}

}        


type cancelCtx struct {

    Context

    done chan struct{} // closed by the first cancel call.

    mu       sync.Mutex

    children map[canceler]bool // set to nil by the first cancel call

    err      error             // 当其被cancel时将会把err设置为非nil

}


func (c *cancelCtx) Done() <-chan struct{} {

    return c.done

}


func (c *cancelCtx) Err() error {

    c.mu.Lock()

    defer c.mu.Unlock()

    return c.err

}


func (c *cancelCtx) String() string {

    return fmt.Sprintf("%v.WithCancel", c.Context)

}


//核心是关闭c.done

//同时会设置c.err = err, c.children = nil

//依次遍历c.children,每个child分别cancel

//如果设置了removeFromParent,则将c从其parent的children中删除

func (c *cancelCtx) cancel(removeFromParent bool, err error) {

    if err == nil {

        panic("context: internal error: missing cancel error")

    }

    c.mu.Lock()

    if c.err != nil {

        c.mu.Unlock()

        return // already canceled

    }

    c.err = err

    close(c.done)

    for child := range c.children {

        // NOTE: acquiring the child's lock while holding parent's lock.

        child.cancel(false, err)

    }

    c.children = nil

    c.mu.Unlock()


    if removeFromParent {

        removeChild(c.Context, c) // 从此处可以看到 cancelCtx的Context项是一个类似于parent的概念

    }

}

timerCtx 结构继承 cancelCtx

type timerCtx struct {

    cancelCtx //此处的封装为了继承来自于cancelCtx的方法,cancelCtx.Context才是父亲节点的指针

    timer *time.Timer // Under cancelCtx.mu. 是一个计时器

    deadline time.Time

}

valueCtx 结构继承 cancelCtx

type valueCtx struct {

    Context

    key, val interface{}

}

主要方法

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key interface{}, val interface{}) Context
 

WithCancel 对应的是 cancelCtx ,其中,返回一个 cancelCtx ,同时返回一个 CancelFunc,CancelFunc 是 context 包中定义的一个函数类型:type CancelFunc func()。调用这个 CancelFunc 时,关闭对应的c.done,也就是让他的后代goroutine退出。

WithDeadline 和 WithTimeout 对应的是 timerCtx ,WithDeadline 和 WithTimeout 是相似的,WithDeadline 是设置具体的 deadline 时间,到达 deadline 的时候,后代 goroutine 退出,而 WithTimeout 简单粗暴,直接 return WithDeadline(parent, time.Now().Add(timeout))。

WithValue 对应 valueCtx ,WithValue 是在 Context 中设置一个 map,拿到这个 Context 以及它的后代的 goroutine 都可以拿到 map 里的值。

详细 context 包源码解读:go源码解读

使用原则

使用 Context 的程序包需要遵循如下的原则来满足接口的一致性以及便于静态分析

不要把 Context 存在一个结构体当中,显式地传入函数。Context 变量需要作为第一个参数使用,一般命名为ctx

即使方法允许,也不要传入一个 nil 的 Context ,如果你不确定你要用什么 Context 的时候传一个 context.TODO

使用 context 的 Value 相关方法只应该用于在程序和接口中传递的和请求相关的元数据,不要用它来传递一些可选的参数

同样的 Context 可以用来传递到不同的 goroutine 中,Context 在多个goroutine 中是安全的

使用示例

例子copy自: 关于 Golang 中的 context 包的介绍

package main


import (

    "fmt"

    "time"

    "golang.org/x/net/context"

)


// 模拟一个最小执行时间的阻塞函数

func inc(a int) int {

    res := a + 1                // 虽然我只做了一次简单的 +1 的运算,

    time.Sleep(1 * time.Second) // 但是由于我的机器指令集中没有这条指令,

    // 所以在我执行了 1000000000 条机器指令, 续了 1s 之后, 我才终于得到结果。B)

    return res

}


// 向外部提供的阻塞接口

// 计算 a + b, 注意 a, b 均不能为负

// 如果计算被中断, 则返回 -1

func Add(ctx context.Context, a, b int) int {

    res := 0

    for i := 0; i < a; i++ {

        res = inc(res)

        select {

        case <-ctx.Done():

            return -1

        default:

        }

    }

    for i := 0; i < b; i++ {

        res = inc(res)

        select {

        case <-ctx.Done():

            return -1

        default:

        }

    }

    return res

}


func main() {

    {

        // 使用开放的 API 计算 a+b

        a := 1

        b := 2

        timeout := 2 * time.Second

        ctx, _ := context.WithTimeout(context.Background(), timeout)

        res := Add(ctx, 1, 2)

        fmt.Printf("Compute: %d+%d, result: %d\n", a, b, res)

    }

    {

        // 手动取消

        a := 1

        b := 2

        ctx, cancel := context.WithCancel(context.Background())

        go func() {

            time.Sleep(2 * time.Second)

            cancel() // 在调用处主动取消

        }()

        res := Add(ctx, 1, 2)

        fmt.Printf("Compute: %d+%d, result: %d\n", a, b, res)

    }

}

Golang的跨平台编译程序

Golang支持交叉编译,也就是说你在32位平台的机器上开发,可以编译生成64位平台上的可执行程序。

交叉编译依赖下面几个环境变量:

$GOARCH    目标平台(编译后的目标平台)的处理器架构(386、amd64、arm)
$GOOS          目标平台(编译后的目标平台)的操作系统(darwin、freebsd、linux、windows)


各平台的GOOS和GOARCH参考 

OS                   ARCH                          OS version

linux                386 / amd64 / arm             >= Linux 2.6

darwin               386 / amd64                   OS X (Snow Leopard + Lion)

freebsd              386 / amd64                   >= FreeBSD 7

windows              386 / amd64                   >= Windows 2000


跨平台编译步骤:

一、了解目标平台架构:

下面几个命令有助于了解目标平台的架构:

[root@localhost ~]# uname -a
Linux localhost.localdomain 2.6.32-279.19.1.el6.x86_64 #1 SMP Wed Dec 19 07:05:20 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
[root@localhost ~]# uname -m
x86_64
[root@localhost ~]# arch
x86_64
[root@localhost ~]# file /bin/cat
/bin/cat: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped
[root@localhost ~]#

这里可以看到这个服务器的架构是 x86_64 这个架构亦称 amd64.

  • 6g是amd64的go编译器,它生成的是.6文件。
  • 386一般使用8g命令,它生成的一般是.8格式的文件。
  • 当然还有一个5g的命令是用于arm的cpu,

同理amd64用6l,386用8l,arm用5l的链接器

参考资料:

Linux下如何查看系统是32位还是64位的?
http://blog.csdn.net/whucs_b701/article/details/8543499

 

 

二、准备目标平台需要的包和工具文件

执行下面命令:

$ cd /usr/local/go/src
$ sudo CGO_ENABLED=0 GOOS=linux GOARCH=amd64 ./make.bash

这里 额外多一个环境变量 CGO_ENABLED 是因为 交叉编译不支持 CGO,我们这里禁用它。

这里并不是重新编译Go,因为安装Go的时候,只是编译了本地系统需要的东西;而需要跨平台交叉编译,需要在Go中增加对其他平台的支持。所以,有 ./make.bash 这么一个过程。

跨平台编译Go程序(交叉编译)

各平台的GOOS和GOARCH参考

OS ARCH OS version linux 386 / amd64 / arm >= Linux 2.6 darwin 386 / amd64 OS X (Snow Leopard + Lion) freebsd 386 / amd64 >= FreeBSD 7 windows 386 / amd64 >= Windows 2000

$ cd /usr/local/go/src $ sudo CGO_ENABLED=0 GOOS=linux GOARCH=amd64 ./make.bash

这里并不是重新编译Go,因为安装Go的时候,只是编译了本地系统需要的东西;而需要跨平台交叉编译,需要在Go中增加对其他平台的支持。所以,有 ./make.bash 这么一个过程。

 

(1)首先进入go/src 源码所在目录,执行如下命令创建目标平台所需的包和工具文件。

$ cd /usr/local/go/src 
$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 ./make.bash

如果是 Windows 则修改 GOOS 即可。 
$ CGO_ENABLED=0 GOOS=windows GOARCH=amd64 ./make.bash

(2) 现在可以编译 Linux 和 Windows 平台所需的执行文件了。

$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build 
$ CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build

不过该方式暂时不支持 CGO。

http://solovyov.net/en/2012/03/09/cross-compiling-go/

以上假定是64位架构,32位,修改GOARCH为386

说明: 
这里并不是重新编译Go,因为安装Go的时候,只是编译了本地系统需要的东西;而需要跨平台交叉编译,需要在Go中增加对其他平台的支持。所以,有 ./make.bash 这么一个过程

交叉编译问题补充:

首先,建议安装Go语言通过源码安装,否则可能没有make.bash或make.bat程序。

之所以需要执行上面的这些步骤,是因为安装Go语言时,一些工具和平台相关的代码并没有生成,执行上面的步骤,并不是重新安装Go语言,而是生成交叉编译(目标平台)需要的工具和文件。这些只是在第一次交叉编译的时候做。之后就不需要了。

为了更快的编译,可以

    ./make.bash --no-clean
Records:47123456