利用pointer, atomic原子操作.实现并发只获取一个操作对象.

import (
    "fmt"
    "sync/atomic"
    "testing"
    "time"
    "unsafe"
)
//使用自旋获取对象. 保证只有一个对象.
type EtcdClient struct {
    username string
    password string
    id int
}
func NewEtcdClient() *EtcdClient {
    return &EtcdClient{
        username:"etcd",
        password:"root!root",
        id: 100,
    }
}
var etcdClientPointer unsafe.Pointer

func Client() *EtcdClient {
    for {
        p := (*EtcdClient)(atomic.LoadPointer(&etcdClientPointer)) //如果指针存在则直接返回
        if p != nil {
            return p
        }
        //不存在则创建
        c := NewEtcdClient()
        //使用比较并载入操作
        if !atomic.CompareAndSwapPointer(&etcdClientPointer, nil, unsafe.Pointer(&c)) {
            time.Sleep(time.Nanosecond * 10) //休10纳秒
            continue
        }
        return c
    }
}

测试用例
获取得地址是一样的.

func TestNewEtcdClient(t *testing.T) {
    for i := 0;i < 10; i ++ {
        go func(i int) {
            fmt.Printf("%d, %p\n",i, Client())
        }(i)
    }
    time.Sleep(time.Second)
}

结果

0xc000062240 #这个地址与下面地址不一样, 这是因为这个地址是对象实际存储的地址
0xc000006018 # 以下地址是Pointer指针存储空间的地址.
0xc000006018
0xc000006018
0xc000006018
0xc000006018
0xc000006018
0xc000006018
0xc000006018
0xc000006018