Xiaopei's DokuWiki

These are the good times in your life,
so put on a smile and it'll be alright

User Tools

Site Tools


it:golang

Golang / Go

import "database/sql"

scan & value

// NullString represents a string that may be null.
// NullString implements the Scanner interface so
// it can be used as a scan destination:
//
//  var s NullString
//  err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
//  ...
//  if s.Valid {
//     // use s.String
//  } else {
//     // NULL value
//  }
//
type NullString struct {
	String string
	Valid  bool // Valid is true if String is not NULL
}
 
// Scan implements the Scanner interface.
func (ns *NullString) Scan(value interface{}) error {
	if value == nil {
		ns.String, ns.Valid = "", false
		return nil
	}
	ns.Valid = true
	return convertAssign(&ns.String, value)
}
 
// Value implements the driver Valuer interface.
func (ns NullString) Value() (driver.Value, error) {
	if !ns.Valid {
		return nil, nil
	}
	return ns.String, nil
}

并发/单例/线程安全/锁

要点:

  1. 使用 互斥锁 Mutual exclusion / Mutex
    1. go里分为全局锁 Mutex 和读写锁 RWMutex
    2. Mutex 适于读写不确定的场景,加锁后会阻止读写
    3. RWMutex 适于多线程读单线程写的场景,加锁后会阻止读写,加读锁后会阻止写(?)
  2. 直接读写变量改为 getter/setter
  3. getter/setter 中先获取锁,defer 释放锁
 import (
 	"math/rand"
 	"net/http"
+	"sync"
 	"time"
 
// ...
 
 type VolumeServer struct {
 	masterNode   string
+	mnLock       sync.RWMutex
 	pulseSeconds int
 
// ...
 
 	vs := &VolumeServer{
-		masterNode:        masterNode,
 		pulseSeconds:      pulseSeconds,
 		dataCenter:        dataCenter,
 		rack:              rack,
 		FixJpgOrientation: fixJpgOrientation,
 	}
+	vs.SetMasterNode(masterNode)
 
// ...
 
-		vs.store.SetBootstrapMaster(vs.masterNode)
+
+		vs.store.SetBootstrapMaster(vs.GetMasterNode())
 
// ...
 
 
+func (vs *VolumeServer) GetMasterNode() string {
+	vs.mnLock.RLock()
+	defer vs.mnLock.RUnlock()
+	return vs.masterNode
+}
+
+func (vs *VolumeServer) SetMasterNode(masterNode string) {
+	vs.mnLock.Lock()
+	defer vs.mnLock.Unlock()
+	vs.masterNode = masterNode
+}
+

优雅退出

var wg sync.WaitGroup
var urls = []string{
        "http://www.golang.org/",
        "http://www.google.com/",
        "http://www.somestupidname.com/",
}
for _, url := range urls {
        // Increment the WaitGroup counter.
        wg.Add(1)
        // Launch a goroutine to fetch the URL.
        go func(url string) {
                // Decrement the counter when the goroutine completes.
                defer wg.Done()
                // Fetch the URL.
                http.Get(url)
        }(url)
}
// Wait for all HTTP fetches to complete.
wg.Wait()

http server

接收上传文件

// func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error)
file, fileHeader, err := c.Request.FormFile("file")
 
// 转 string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
	// fmt.Println(scanner.Text()) // Println will add back the final '\n'
	content += scanner.Text() + "\n"
}
if err := scanner.Err(); err != nil {
	// fmt.Fprintln(os.Stderr, "reading standard input:", err)
}	

http client

测试 / testing / test

运行测试:

# Run all tests.
$ go test
$ go test -run ''      
 
# 查看用例执行详情
$ go test -v
 
# 查看覆盖率
$ go test --cover
 
$ go test -run Foo     # Run top-level tests matching "Foo", such as "TestFooBar".
$ go test -run Foo/A=  # For top-level tests matching "Foo", run subtests matching "A=".
$ go test -run /A=1    # For all top-level tests, run subtests matching "A=1".

类型转换 / Conversions / type casting / type assertion

  • func ParseInt(s string, base int, bitSize int) (i int64, err error)

    ParseInt interprets a string s in the given base (2 to 36) and returns the corresponding value i. If base == 0, the base is implied by the string's prefix: base 16 for “0x”, base 8 for “0”, and base 10 otherwise.

    The bitSize argument specifies the integer type that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 correspond to int, int8, int16, int32, and int64.

  • func FormatInt(i int64, base int) string

    FormatInt returns the string representation of i in the given base, for 2 ⇐ base ⇐ 36. The result uses the lower-case letters 'a' to 'z' for digit values >= 10.

# The most common numeric conversions are Atoi (string to int) and Itoa (int to string).
# These assume decimal and the Go int type.
 
i, err := strconv.Atoi("-42")
s := strconv.Itoa(-42)
 
 
# ParseBool, ParseFloat, ParseInt, and ParseUint convert strings to values:
 
b, err := strconv.ParseBool("true")
f, err := strconv.ParseFloat("3.1415", 64)
i, err := strconv.ParseInt("-42", 10, 64)
u, err := strconv.ParseUint("42", 10, 64)
 
# The parse functions return the **widest type (float64, int64, and uint64)**, 
# but if the size argument specifies a narrower width the result can be 
# converted to that narrower type without data loss:
 
s := "2147483647" // biggest int32
i64, err := strconv.ParseInt(s, 10, 32)
i := int32(i64)
 
# FormatBool, FormatFloat, FormatInt, and FormatUint convert values to strings:
 
s := strconv.FormatBool(true)
s := strconv.FormatFloat(3.1415, 'E', -1, 64)
s := strconv.FormatInt(-42, 16)
s := strconv.FormatUint(42, 16)

concurrency

buffered channel

The buffer size is the number of elements that can be sent to the channel without the send blocking. By default, a channel has a buffer size of 0 (you get this with make(chan int)). This means that every single send will block until another goroutine receives from the channel. A channel of buffer size 1 can hold 1 element until sending blocks.

package main
 
import "fmt"
 
func main() {
	// 这是一个单 goroutine 的程序
 
	ch := make(chan int) // buffer size 0
	ch <- 1              // 第一个就会报错
	// fatal error: all goroutines are asleep - deadlock!
	// goroutine 1 [chan send]
	// exit status 2
 
	ch := make(chan int, 1) // buffer size 1
	ch <- 1                 // 第一个能写入
	ch <- 2                 // 第二个会报错
	// fatal error: all goroutines are asleep - deadlock!
	// goroutine 1 [chan send]
	// exit status 2
 
	ch := make(chan int, 2) // buffer size 2,最多能写 2 个
 
	// 写两个读两个正常
	ch <- 1
	ch <- 2
	fmt.Println(<-ch)
	fmt.Println(<-ch)
 
	// 写一个读一个也正常
	ch <- 1
	ch <- 2
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

select(注意不是 switch)和一个疑问

package main
 
import "fmt"
 
func fib(c, quit chan int) {
	x, y := 0, 1
	for {
		// The select statement lets a goroutine wait on
		// multiple communication operations.
		//
		// A select blocks until one of its cases can run,
		// then it executes that case. It chooses one at
		// random if multiple are ready.
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}
 
func main() {
	c := make(chan int)
	quit := make(chan int)
 
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c) // 2. goroutine 里写 channel
		}
		quit <- 0 // 3. 万一还没运行到 fib 就先写了怎么办?
	}()
	fib(c, quit) // 1. 先注册监听 channel
}

可能能解答上面的问题

c := make(chan int)  // Allocate a channel.
// Start the sort in a goroutine; when it completes, signal on the channel.
go func() {
    list.Sort()
    c <- 1  // Send a signal; value does not matter.
}()
doSomethingForAWhile()
<-c   // Wait for sort to finish; discard sent value.
 
// go 应该是 main 运行到底就退出,不管 goroutine
it/golang.txt · Last modified: 2018/05/25 19:45 by admin