這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Error handling
type error interface { Error() string}
var errNotFound error = errors.New("Not found error")err := errors.New("math – square root of negative number")
type PathError struct { Op string Path string Err error}
panic("A severe error occurred: stopping the program!")
package parse// importtype ParseError struct { Index int Word string Error err}func (e *ParseError) String() string { return fmt.Sprintf("...error parsing %q as int", e.Word)}func Parse(input string) (numbers []int, err error) { defer func() { if r := recover(); r != nil { var ok bool err, ok = r.(error) if !ok { err = fmt.Errorf("pkg: %v", r) } } }() fields := strings.Fields(input) numbers = fields2numbers(fields) return}func fields2numbers(fields []string) (numbers []int) { if len(fields) == 0 { panic("no words to parse") } for idx, field := range fields { num, err := strconv.Atoi(field) if err != nil { panic(&ParseError{idx, field, err}) } numbers = append(numbers, num) } return}
package mainimport ( "fmt" "./parse/parse")func main() { var examples = []string{"1 2 3 4 5", ..., } for _, ex := range examples { fmt.Printf("Parsing %q:\n ", ex) nums, err := parse.Parse(ex) if err != nil { fmt.Println(err) continue } fmt.Println(nums) }}
Starting an external command or program
package mainimport ( "fmt" "os/exec" "os" ) func main() { // os.StartProcess env := os.Environ() procAttr := &os.ProcAttr{ Env: env, Files: []*os.File{ os.Stdin, os.Stdout, os.Stderr, }, } pid, err := os.StartProcess("/bin/ls", []string{"ls", "-l"}, procAttr) if err != nil { fmt.Printf("Error %v starting process!", err) // os.Exit(1) } fmt.Printf("The process id is %v", pid) ...
cmd := exec.Command("gedit") // this opens a gedit-windowerr := cmd.Run()if err != nil { fmt.Printf("Error %v executing command!", err) os.Exit(1)}fmt.Printf("The command is %v", cmd)
Testing and Debugging
where := func() { _, file, line, _ := runtime.Caller(1) log.Printf("%s:%d", file, line)}
runtime.GC()fmt.Printf("%d\n", runtime.MemStats.Alloc/1024)runtime.SetFinalizer(obj, func(obj *typeObj))
func (t *T) Fail() ü marks the test function as having failed, but continues its execution.
func (t *T) FailNow() ü marks the test function as having failed and stops its execution; ü all other tests in this file are also skipped, execution continues with the next test file
func (t *T) Log(args …interface{}) ü the args are formatted using default formatting and the text is logged in the error-log
func (t *T) Fatal(args …interface{}) ü this has the combined effect of c) followed by b)
|
ü test programs must be within the same package ü the files must have names of the form *_test.go |
package mainimport ( "fmt" "./even/even")func main() { for i:=0; i<=100; i++ { fmt.Printf("Is the integer %d even? %v\n", i, even.Even(i)) }}
package evenfunc Even(i int) bool { // Exported functions return i%2 == 0}func Odd(i int) bool { return i%2 != 0}
package evenimport "testing"func TestEven(t *testing.T) { if !Even(10) { t.Log("10 must be even!") t.Fail() } if Even(7) { t.Log("7 is not even!") t.Fail() }}func TestOdd(t *testing.T) { if !Odd(11) { t.Log("11 must be odd!") t.Fail() } if Odd(10) { t.Log("10 is not odd!") t.Fail() }}
// Using table-driven testsvar tests = []struct{ // Test table in string out string}{ {"in1", "exp1"}, {"in2", "exp2"}, ...}func TestFunction(t *testing.T) { for i, tt := range tests { s := FuncToBeTested(tt.in) verify(t, i, "FuncToBeTested: ", tt.in, s, tt.out) }}func verify(t *testing.T, testnum int, testcase, input, output, expected string) { if input != output { t.Errorf("%d. %s with input = %s: output %s != %s", testnum, testcase, input, output, expected) }}
// investigating performance// go test –x –v –cpuprofile=cpuprof.out –file x_test.go// go test –x –v –memprofile=memprof.out –file x_test.govar cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")func main() { flag.Parse() if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } ...// progexec -cpuprofile=progexec.prof// gopprof progexec progexec.prof// Some of the interesting commands of this tool are:// a) topN: shows the top N samples in the profile// b) web or web funcname: writes a graph of the profile data in SVG format// list funcname or weblist funcname
// If it is seen that the function runtime.mallocgc (which both allocates and runs periodic garbage // collections) is heavily used, then it is time for memory profiling.// var memprofile = flag.String("memprofile", "", "write memory profile to this file")// ...CallToFunctionWhichAllocatesLotsOfMemory()if *memprofile != "" { f, err := os.Create(*memprofile) if err != nil { log.Fatal(err) } pprof.WriteHeapProfile(f) f.Close() return}// progexec -memprofile=progexec.mprof// gopprof progexec progexec.mprof// gopprof --inuse_objects progexec progexec.mprof
// For web applications:import _ "http/pprof"// gopprof http://localhost:6060/debug/pprof/profile # 30-second CPU profile// gopprof http://localhost:6060/debug/pprof/heap # heap profile
Goroutines
runtime.Gosched() / runtime.Goexit()
An experiential rule of thumb seems to be that for n cores setting GOMAXPROCS to n-1 yields the best performance, and the following should also be followed: number of goroutines > 1 + GOMAXPROCS > 1
|
var numCores = flag.Int("n", 2, "number of CPU cores to use")runtime.GOMAXPROCS(*numCores)
func main() { go longWait() go shortWait() fmt.Println("About to sleep in main()") time.Sleep(10 * 1e9) fmt.Println("At the end of main()")}func longWait() { fmt.Println("Beginning longWait()") time.Sleep(5 * 1e9) // sleep for 5 seconds fmt.Println("End of longWait()")}func shortWait() { fmt.Println("Beginning shortWait()") time.Sleep(2 * 1e9) // sleep for 2 seconds fmt.Println("End of shortWait()")}
Channels
var ch1 chan stringch1 = make(chan string)ch1 := make(chan string)buf := 100ch1 := make(chan string, buf)chanOfChans := make(chan chan int)funcChan := chan func()
func main() { ch := make(chan string) go sendData(ch) go getData(ch) time.Sleep(1e9)}func sendData(ch chan string) { ch <- “Washington” ch <- “Tripoli” ch <- “London”}func getData(ch chan string) { var input string for { input = <-ch; fmt.Printf("%s ", input) }}
type Empty interface {}var empty Empty...data := make([]float64, N)res := make([]float64, N)sem := make(chan Empty, N) // semaphore ...for i, xi := range data { go func (i int, xi float64) { res[i] = doSomething(i,xi) sem <- empty } (i, xi)}for i := 0; i < N; i++ { // wait for goroutines to finish <-sem }
func main() { stream := pump() go suck(stream) // shortened : go suck( pump() ) time.Sleep(1e9)}func pump() chan int { ch := make(chan int) go func() { for i := 0; ; i++ { ch <- i } }() return ch}func suck(ch chan int) { for { fmt.Println(<-ch) }}func suck(ch chan int) { go func() { for v := range ch { fmt.Println(v) } }()}
// channel can only receive data and cannot be closedvar send_only chan<- intvar recv_only <-chan int // channel can only send data...var c = make(chan int) // bidirectionalgo source(c)go sink(c)func source(ch chan<- int) { for { ch <- 1 }}func sink(ch <-chan int) { for { <-ch }}...// closing a channelfunc sendData(ch chan string) { ch <- "Washington" ch <- "Tripoli" ch <- "London" ch <- "Beijing" ch <- "Tokio" close(ch)}func getData(ch chan string) { for { input, open := <-ch if !open { break } fmt.Printf("%s ", input) }}
select {case u:= <- ch1: ...case v:= <- ch2: ...default: // no value ready to be received ...}
if all are blocked, it waits until one can proceed
if multiple can proceed, it chooses one at random.
when none of the channel operations can proceed and the default clause is present, then this is executed: the default is always runnable (that is: ready to execute). Using a send operation in a select statement with a default case guarantees that the send will be non-blocking!
|
// func Tick(d Duration) <-chan Timeimport "time"rate_per_sec := 10var dur Duration = 1e8 // rate_per_secchRate := time.Tick(dur) // every 1/10th of a secondfor req := range requests { <- chRate // rate limit our Service.Method RPC calls go client.Call("Service.Method", req, ...)}
// func After(d Duration) <-chan Timefunc main() { tick := time.Tick(1e8) boom := time.After(5e8) for { select { case <-tick: fmt.Println(“tick.”) case <-boom: fmt.Println(“BOOM!”) return default: fmt.Println(“ .”) time.Sleep(5e7) } }}
func server(workChan <-chan *Work) { for work := range workChan { go safelyDo(work) }}func safelyDo(work *Work) { defer func() { if err := recover(); err != nil { log.Printf(“work failed with %s in %v:”, err, work) } }() do(work)}