互斥锁 //展示如何使用互斥锁来定义一段 //需要同步访问的代码临界区资源的同步访问 package main import ( "fmt" "runtime" "sync" ) var ( //counter 是所有goroutine都要增加其值的变量 counter int //wg 用来等待程序结束 wg sync.WaitGroup //mutex 用来定义一段代码的临界区 mutex sync.Mutex ) //main 是go程序入口 func main() { //计数加2,表示要等待两个goroutine wg.Add(2) //创建两个goroutine go incCounter(1) go incCounter(2) //等待goroutine结束 wg.Wait() fmt.Printf("Final Counter: %d\\n",counter) } //incCounter 使用互斥锁来同步并保证安全访问 //增加包里counter变量的值 func incCounter(id int) { //在函数退出时调用Done来通知main函数工作完成 defer wg.Done() for count := 0;count <2;count++ { //同一时刻只允许一个goroutine进入这个临界区 mutex.Lock() { //捕获counter的值 value := counter //当前goroutine从线程退出,并放回队列 runtime.Gosched() //增加本地value变量的值 value++ //将该值保存回counter counter = value } mutex.Unlock() //释放锁,允许其他正在等待的goroutine进入临界区 } } 通道 通过通道可以共享内置类型,命名类型,结构类型,和引用类型的值或指针 使用make创建通道 //无缓冲通道 umbuggered := make(chan int) //有缓冲的字符串通道 buffered := make(chan string,10) 向通道发送值,接收值 //创建有缓冲的字符串通道 buffered := make(chan string,10) //通过通道发送一个字符串 buffered <- "Gopher" //从通道接收一个字符串 value := <-buffered 无缓冲通道 在接收前没有能力保存任何值得通道 要求发送goroutine和接收goroutine同时进行才能完成发送和接收操作 示例: //展示使用无换从得通道来模拟2个goroutine见得网球比赛 package main import ( "fmt" "math/rand" "sync" "time" ) //wg 用来等待程序结束 var wg sync.WaitGroup func init() { rand.Seed(time.Now().UnixNano()) } //main 是程序得入口 func main() { //创建一个无缓冲的通道 court := make(chan int) //计数加2,表示要等待2个goroutine wg.Add(2) //启动两个选手 go player("Nadal",court) go player("Djokovic",court) //发球 court <- 1 //等待游戏结束 wg.Wait() } //player 模拟一个选手在打网球 func player(name string,court chan int) { //在函数退出时调用Done来通知main函数工作已经完成 defer wg.Done() for { //等待球被击打过来 ball,ok := <-court if !ok { //如果通道被关闭,我们就赢了 fmt.Printf("Player %s Won\n",name) return } //选随机数,然后用这个数来判断我们是否丢球 n := rand.Intn(100) if n % 13 == 0 { fmt.Printf("Player %s Missed\n",name) //关闭通道,表示我们输了 close(court) return } //显示击球数,并将击球数加1 fmt.Printf("Player %s Hit %d\n",name,ball) ball++ //将球打向对手 court <- ball } } //展示使用无缓冲通道来模拟4个goroutine间的接力比赛 package main import ( "fmt" "sync" "time" ) //wg 用来等待程序结束 var wg sync.WaitGroup //main 是程序的入口 func main() { //创建一个无缓冲的通道 baton := make(chan int) //为最后一位跑步者将计数加1 wg.Add(1) //第一位跑步者持有接力棒 go Runner(baton) //开始比赛 baton <- 1 //等待比赛结束 wg.Wait() } //Runner模拟接力比赛中的一位跑步者 func Runner(baton chan int) { var newRunner int //等待接力棒 runner := <-baton //开始绕着跑道跑步 if runner != 4 { newRunner = runner + 1 fmt.Printf("Runner %d To The Line\n",newRunner) go Runner(baton) } //围绕跑道跑 time.Sleep(100 * time.Millisecond) //比赛结束了吗? if runner == 4 { fmt.Printf("Runner %d Finished,Race Over\n", runner) wg.Done() return } //将接力棒交给下一位跑步者 fmt.Printf("Runner %d Exchange With Runner %d\n", runner, newRunner) baton <- newRunner } 有缓冲通道 被接收前能存储一个或多个值得通道 示例: //展示使用有缓冲通道和固定数目的goroutine来处理工作 package main import ( "fmt" "math/rand" "sync" "time" ) const ( numberGoroutines = 4 //要使用goroutine的数量 taskLoad = 10 //要处理的工作数量 ) //wg 用来等待程序完成 var wg sync.WaitGroup //init 初始化包,go语言运行时会在其他代码执行之前 //优先执行这个函数 func init() { //初始化随机数种子 rand.Seed(time.Now().Unix()) } //main 是程序入口 func main() { //创建一个有缓冲的通道来管理工作 tasks := make(chan string,taskLoad) //启动goroutine来处理工作 wg.Add(numberGoroutines) for gr := 1;gr <= numberGoroutines;gr++ { go worker(tasks,gr) } //增加一组要完成的工作 for post := 1;post <= taskLoad; post++ { tasks <- fmt.Sprintf("Task : %d",post) } //当所有的工作都处理完成时关闭通道 //以便所有的goroutine退出 close(tasks) //等待所有工作完成 wg.Wait() } //worker 作为goroutine启动来处理 //从有缓冲的通道传入的工作 func worker(tasks chan string,worker int) { //通知函数已经返回 defer wg.Done() for { //等待分配工作 task,ok := <-tasks if !ok { //这意味通道已经空了,并且已被关闭 fmt.Printf("Worker: %d : Shutting Down\n",worker) return } //显示开始工作 fmt.Printf("Worker : %d : Started %s\n",worker,task) //随机等一段时间来模拟工作 sleep := rand.Int63n(100) time.Sleep(time.Duration(sleep)*time.Millisecond) //显示完成工作 fmt.Printf("Worker: %d : Completed %s\n",worker,task) } } 并发模式 runner pool work 标准库 文档与源代码 记录日志 log包 示例: // 这个示例程序展示如何使用最基本的 log 包 package main import ( "log" ) func init() { log.SetPrefix("TRACE: ") log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile) } func main() { // Println 写到标准日志记录器 log.Println("message") // Fatalln 在调用 Println()之后会接着调用 os.Exit(1) log.Fatalln("fatal message") // Panicln 在调用 Println()之后会接着调用 panic() log.Panicln("panic message") } 定制的日志记录器 示例: //展示如何创建定制的日志记录器 package main import ( "io" "io/ioutil" "log" "os" ) var ( Trace *log.Logger //记录所有日志 Info *log.Logger //重要的信息 Warning *log.Logger //需要注意的信息 Error *log.Logger //菲常严重的问题 ) func init() { file,err := os.OpenFile("errors.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND,0666) if err != nil { log.Fatalln("Fail to open error log file:",err) } Trace = log.New(ioutil.Discard, "TRACE ", log.Ldate|log.Ltime|log.Lshortfile) Info = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) Warning = log.New(os.Stdout, "WARING: ", log.Ldate|log.Ltime|log.Lshortfile) Error = log.New(io.MultiWriter(file,os.Stderr), "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) } func main() { Trace.Println("I have something standard to say") Info.Println("Special Information") Warning.Println("There is someting you need to know about") Error.Println("Someting has faild") } 编码/解码 解码JSON 示例: //展示使用json包和NewDecoder函数来解码JSON响应 package main import ( "encoding/json" "fmt" "log" "net/http" ) type ( //gResult 映射到从搜索拿到的结果文档 gResult struct { GsearchResultClass string `json:"GsearchResultClass"` UnescapedURL string `json:"unescapedUrl"` URL string `json:"url"` VisibleURL string `json:"VisibleUrl"` CacheURL string `json:"cacheUrl"` Title string `json:"title"` TitleNoFormatting string `json:"titleNoFormatting"` Content string `json:"content"` } //gResponse 包含顶级的文档 gResponse struct { ResponseData struct{ Results []gResult `json:"results"` } `json:"responseData"` } ) func main() { uri := "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=8&q=golang" //向Google 发起搜索 resp,err := http.Get(uri) if err != nil { log.Println("ERROR:",err) return } defer resp.Body.Close() //将JSON 响应解码到结构类型 var gr gResponse err = json.NewDecoder(resp.Body).Decode(&gr) if err != nil { log.Println("ERROR:",err) return } fmt.Println(gr) } //展示如何解码JSON字符串 package main import ( "encoding/json" "fmt" "log" ) //Contact 结构代表JSON字符串 type Contact struct { Name string `json:"name"` Title string `json:"title"` Contact struct { Home string `json:"home"` Cell string `json:"cell"` } `json:"contact"` } //JSON 包含用于反序列化的演示字符串 var JSON = `{ "name": "Gopher", "title": "programmer", "contact": { "home": "415.333.3333", "cell": "415.555.5555" } }` func main() { //将JSON字符串反序列化到变量 var c Contact err := json.Unmarshal([]byte(JSON),&c) if err != nil { log.Println("ERROR:",err) return } fmt.Println(c) } //将JSON文档解码到一个map变量中 //展示解码JSON字符串 package main import ( "encoding/json" "fmt" "log" ) // JSON 包含要反序列化的样例字符串 var JSON = `{ "name": "Gopher", "title": "programmer", "contact": { "home": "415.333.3333", "cell": "415.555.5555" } }` func main() { // 将 JSON 字符串反序列化到 map 变量 var c map[string]interface{} err := json.Unmarshal([]byte(JSON), &c) if err != nil { log.Println("ERROR:", err) return } fmt.Println("Name:", c["name"]) fmt.Println("Title:", c["title"]) fmt.Println("Contact") fmt.Println("H:", c["contact"].(map[string]interface{})["home"]) fmt.Println("C:", c["contact"].(map[string]interface{})["cell"]) } 编码JSON 将Go语言的map类型的值或者结构类型的值转换为易读格式的json文档 示例: //将map类型转换为JSON字符串 //展示序列化JSON字符串 package main import ( "encoding/json" "fmt" "log" ) func main() { // 创建一个保存键值对的映射 c := make(map[string]interface{}) c["name"] = "Gopher" c["title"] = "programmer" c["contact"] = map[string]interface{}{ "home": "415.333.3333", "cell": "415.555.5555", } // 将这个映射序列化到 JSON 字符串 data, err := json.MarshalIndent(c, "", " ") if err != nil { log.Println("ERROR:", err) return } fmt.Println(string(data)) } 输入和输出 Writer 和 Reader 接口 简单的 curl 示例: // 这个示例程序展示如何使用 io.Reader 和 io.Writer 接口 // 写一个简单版本的 curl package main import ( "io" "log" "net/http" "os" ) // main 是应用程序的入口 func main() { // 这里的 r 是一个响应,r.Body 是 io.Reader //r, err := http.Get(os.Args[1]) r, err := http.Get(`https://blog.csdn.net/liao__ran?spm=1000.2115.3001.5343`) if err != nil { log.Fatalln(err) } // 创建文件来保存响应内容 //file, err := os.Create(os.Args[2]) file, err := os.Create(`.\a.txt`) if err != nil { log.Fatalln(err) } defer file.Close() // 使用 MultiWriter,这样就可以同时向文件和标准输出设备 // 进行写操作 dest := io.MultiWriter(os.Stdout, file) // 读出响应的内容,并写到两个目的地 io.Copy(dest, r.Body) if err := r.Body.Close(); err != nil { log.Println(err) } } 测试和性能 单元测试 测试包或者程序的一部分代码或者一组代码的函数 基础单元测试 示例: // 这个示例程序展示如何写基础单元测试 package listing01 import ( "net/http" "testing" ) const checkMark = "\u2713" const ballotX = "\u2717" // TestDownload 确认 http 包的 Get 函数可以下载内容 func TestDownload(t *testing.T) { url := "http://www.goinggo.net/feeds/posts/default?alt=rss" statusCode := 200 t.Log("Given the need to test downloading content.") { t.Logf("\tWhen checking \"%s\" for status code \"%d\"", url, statusCode) { resp, err := http.Get(url) if err != nil { t.Fatal("\t\tShould be able to make the Get call.", ballotX, err) } t.Log("\t\tShould be able to make the Get call.", checkMark) defer resp.Body.Close() if resp.StatusCode == statusCode { t.Logf("\t\tShould receive a \"%d\" status. %v", statusCode, checkMark) } else { t.Errorf("\t\tShould receive a \"%d\" status. %v %v", statusCode, ballotX, resp.StatusCode) } } } } 测试结果: D:\Go\Go学习\go语言实战\项目测试>go test -v === RUN TestDownload --- FAIL: TestDownload (1.58s) listing01_test.go:13: Given the need to test downloading content. listing01_test.go:15: When checking "http://www.goinggo.net/feeds/posts/default?alt=rss" for status code "200" listing01_test.go:23: Should be able to make the Get call. ✓ listing01_test.go:30: Should receive a "200" status. ✗ 404 FAIL exit status 1 FAIL _/D_/Go/Go学习/go语言实战/项目测试 1.644s 表组测试 示例: // 这个示例程序展示如何写一个基本的表组测试 package listing08 import ( "net/http" "testing" ) const checkMark = "\u2713" const ballotX = "\u2717" // TestDownload 确认 http 包的 Get 函数可以下载内容 // 并正确处理不同的状态 func TestDownload(t *testing.T) { var urls = []struct { url string statusCode int }{ { "http://www.goinggo.net/feeds/posts/default?alt=rss", http.StatusOK, }, { "http://rss.cnn.com/rss/cnn_topstbadurl.rss", http.StatusNotFound, }, } t.Log("Given the need to test downloading different content.") { for _, u := range urls { t.Logf("\tWhen checking \"%s\" for status code \"%d\"", u.url, u.statusCode) { resp, err := http.Get(u.url) if err != nil { t.Fatal("\t\tShould be able to Get the url.", ballotX, err) } t.Log("\t\tShould be able to Get the url", checkMark) defer resp.Body.Close() if resp.StatusCode == u.statusCode { t.Logf("\t\tShould have a \"%d\" status. %v", u.statusCode, checkMark) } else { t.Errorf("\t\tShould have a \"%d\" status %v %v", u.statusCode, ballotX, resp.StatusCode) } } } } } D:\Go\Go学习\go语言实战\项目测试>go test -v listing01_test.go === RUN TestDownload --- FAIL: TestDownload (9.75s) listing01_test.go:29: Given the need to test downloading different content. listing01_test.go:32: When checking "http://www.goinggo.net/feeds/posts/default?alt=rss" for status code "200" listing01_test.go:40: Should be able to Get the url ✓ listing01_test.go:49: Should have a "200" status ✗ 404 listing01_test.go:32: When checking "http://rss.cnn.com/rss/cnn_topstbadurl.rss" for status code "404" listing01_test.go:37: Should be able to Get the url. ✗ Get http://rss.cnn.com/rss/cnn_topstbadurl.rss: read tcp 192.168.92.16:60817->172.217.160.83:80: wsarecv: An existing connection was forcibly closed by the remote host. FAIL FAIL command-line-arguments 9.819s FAIL 模仿调用 示例: // 这个示例程序展示如何内部模仿 HTTP GET 调用 // 与本书之前的例子有些差别 package listing12 import ( "fmt" "net/http" "net/http/httptest" ) const checkMark = "\u2713" const ballotX = "\u2717" // feed 模仿了我们期望接收的 XML 文档 var feed = `<?xml version="1.0" encoding="UTF-8"?> <rss> <channel> <title>Going Go Programming</title> <description>Golang : https://github.com/goinggo</description> <link>http://www.goinggo.net/</link> <item> <pubDate>Sun, 15 Mar 2015 15:04:00 +0000</pubDate> <title>Object Oriented Programming Mechanics</title> <description>Go is an object oriented language.</description> <link>http://www.goinggo.net/2015/03/object-oriented</link> </item> </channel> </rss>` // mockServer 返回用来处理请求的服务器的指针 func mockServer() *httptest.Server { f := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) w.Header().Set("Content-Type", "application/xml") fmt.Fprintln(w, feed) } return httptest.NewServer(http.HandlerFunc(f)) } D:\Go\Go学习\go语言实战\项目测试>go test -v listing01_test.go testing: warning: no tests to run PASS ok command-line-arguments 0.056s [no tests to run] 测试服务端点 基准测试 测试代码性能的方法 可以用来识别某段代码的cpu或者内存效率问题 可以测试不同的并发模式 辅助配置工作池的数量,保证能最大话系统的吞吐量
Go语言实战--笔记3
猜你喜欢
转载自blog.csdn.net/liao__ran/article/details/114397356
今日推荐
周排行