sync和channel不能一起使用么?

wangfeiping · · 2283 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

请教一个golang的问题,sync和channel不能一起使用么?我开发了一个开源的小应用,自动检测和更新版本,不过执行到Wait()好像总是死锁? 代码:https://coding.net/u/wangfeiping/p/Going/git/blob/master/src/GoHttpGet/main.go 问题出现在 374行 到 389行。 我想备份复制文件和下载同时进行,两个都完成之后再继续进行后面的处理。 ```go // GoHttpGet project main.go package main import ( "archive/zip" //"bytes" "encoding/json" "fmt" "io" "io/ioutil" "log" "net/http" "os" "os/exec" "path/filepath" "strings" "sync" ) type ConfigDetail struct { //首字母必须大写,否则无法正常解析 Version string `json:"version"` UpdateTime string `json:"updateTime"` UpdateUrl string `json:"updateUrl"` ServiceName string `json:"serviceName"` DeployPath string `json:"deployPath"` DeployWar string `json:"deployWar"` BackupPath string `json:"backupPath"` } type UpdateDetail struct { //首字母必须大写,否则无法正常解析 Version string `json:"version"` UpdateTime string `json:"updateTime"` DownloadUrl string `json:"downloadUrl"` DeleteContents []string `json:"deleteContents"` } //golang使用http client发起get和post请求示例 http://ju.outofmemory.cn/entry/87147 //golang下载文件 http://outofmemory.cn/code-snippet/32779/originally-golang-file //golang写个windows服务 http://my.oschina.net/u/130746/blog/226050 //windows service操作 https://github.com/kardianos/service /** 用sc可打开被禁用的服务。 sc是用于与服务控制管理器和服务进行通信的命令行程序,其语法是: sc config 服务名 start= demand //手动 sc condig 服务名 start= auto //自动 sc config 服务名 start= disabled //禁用 sc start 服务名 sc stop 服务名 */ func httpGet(url string) (UpdateDetail, string, error) { resp, err := http.Get(url) if err != nil { // handle error } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } str := string(body) fmt.Println(str) var res UpdateDetail //如果使用err := 则会报错“no new variables on left side of :=” //因为:= 表示声明和赋值,同一个变量重复声明是不允许的 //因此改为err = err = json.Unmarshal([]byte(str), &res) return res, str, err } func readFile(filename string) (ConfigDetail, error) { var conf ConfigDetail bytes, err := ioutil.ReadFile(filename) if err != nil { fmt.Println("ReadFile: ", err.Error()) str := `{ "version" : "0" , "updateTime": "2015-03-23 13:50", "updateUrl" : "https://s3-ap-southeast-2.amazonaws.com/3dview-package/update.json" }` err := json.Unmarshal([]byte(str), &conf) return conf, err } if err := json.Unmarshal(bytes, &conf); err != nil { fmt.Println("Unmarshal: ", err.Error()) return conf, err } return conf, nil } func localUpdateDetail(filename string) (UpdateDetail, error) { var conf UpdateDetail bytes, err := ioutil.ReadFile(filename) if err != nil { fmt.Println("ReadFile: ", err.Error()) return conf, err } if err := json.Unmarshal(bytes, &conf); err != nil { fmt.Println("Unmarshal: ", err.Error()) return conf, err } return conf, nil } func download(url, exePath string) (filename string, n int64, err error) { //Go的异常处理 defer, panic, recover http://blog.csdn.net/wuwenxiang91322/article/details/9042503 defer func() { if r := recover(); r != nil { fmt.Println("[E]", r) download(url, exePath) } }() path := strings.Split(url, "/") var name string if len(path) > 1 { name = exePath + path[len(path)-1] } fmt.Println(name) out, err := os.Create(name) defer out.Close() resp, err := http.Get(url) defer resp.Body.Close() //ioutil.ReadAll(resp.Body) 方法会将文件整个读取到内存中后才返回,如果文件较大,经常会造成下载失败 //pix, err := ioutil.ReadAll(resp.Body) //n, err = io.Copy(out, bytes.NewReader(pix)) n, err = io.Copy(out, resp.Body) return name, n, err } func startService(serviceName string) { command := "sc" args := "start " + serviceName argArray := strings.Split(args, " ") cmd := exec.Command(command, argArray...) buf, err := cmd.Output() if err != nil { fmt.Fprintf(os.Stderr, "The command failed to perform: %s (Command: %s, Arguments: %s)", err, command, args) return } fmt.Fprintf(os.Stdout, "Result: %s", buf) } func stopService(serviceName string) { command := "sc" args := "stop " + serviceName argArray := strings.Split(args, " ") cmd := exec.Command(command, argArray...) buf, err := cmd.Output() if err != nil { fmt.Fprintln(os.Stderr, "The command failed to perform: %s (Command: %s, Arguments: %s)", err, command, args) return } fmt.Fprintln(os.Stdout, "Result: %s", buf) } type FileInfo struct { RelPath string Size int64 IsDir bool Handle *os.File } //复制文件数据 func ioCopy(srcHandle *os.File, dstPth string) (err error) { dstHandle, err := os.OpenFile(dstPth, os.O_CREATE|os.O_WRONLY, os.ModePerm) if err != nil { return err } defer srcHandle.Close() defer dstHandle.Close() _, err = io.Copy(dstHandle, srcHandle) return err } //遍历目录,将文件信息传入通道 func WalkFiles(srcDir, suffix string, c chan<- *FileInfo) { suffix = strings.ToUpper(suffix) filepath.Walk(srcDir, func(f string, fi os.FileInfo, err error) error { //遍历目录 if err != nil { log.Println("[E]", err) } fileInfo := &FileInfo{} if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { //匹配文件 if fh, err := os.OpenFile(f, os.O_RDONLY, os.ModePerm); err != nil { log.Println("[E]", err) } else { fileInfo.Handle = fh fileInfo.RelPath, _ = filepath.Rel(srcDir, f) //相对路径 fileInfo.Size = fi.Size() fileInfo.IsDir = fi.IsDir() } c <- fileInfo } return nil }) close(c) //遍历完成,关闭通道 } //写目标文件 func WriteFiles(dstDir string, c <-chan *FileInfo, w sync.WaitGroup) { if _, err := os.Stat(dstDir); os.IsNotExist(err) { log.Println("[I]", "try to mkdir:", dstDir) if err := os.MkdirAll(dstDir, os.ModeDir); err != nil { log.Println("[E]", err) } } if err := os.Chdir(dstDir); err != nil { //切换工作路径 log.Fatalln("[F]", err) } for f := range c { if fi, err := os.Stat(f.RelPath); os.IsNotExist(err) { //目标不存在 if f.IsDir { if err := os.MkdirAll(f.RelPath, os.ModeDir); err != nil { log.Println("[E]", err) } } else { if err := ioCopy(f.Handle, f.RelPath); err != nil { log.Println("[E]", err) } else { //log.Println("[I] CP:", f.RelPath) } } } else if !f.IsDir { //目标存在,而且源不是一个目录 if fi.IsDir() != f.IsDir { //检查文件名被目录名占用冲突 log.Println("[E]", "filename conflict:", f.RelPath) } else if fi.Size() != f.Size { //源和目标的大小不一致时才重写 if err := ioCopy(f.Handle, f.RelPath); err != nil { log.Println("[E]", err) } else { //log.Println("[I] CP:", f.RelPath) } } } } w.Done() fmt.Println("backup current files.") } func unzip(zipfile, tmp string, conf ConfigDetail) { r, err := zip.OpenReader(zipfile) if err != nil { log.Fatal(err) } defer r.Close() for _, f := range r.File { //fmt.Println("FileName : ", f.Name) //rc, err := f.Open() //if err != nil { // log.Fatal(err) //} //_, err = io.CopyN(os.Stdout, rc, 68) //打印文件内容 //if err != nil { // if err != io.EOF { // log.Fatal(err) // } //} fi := f.FileInfo() if fi.IsDir() { //fmt.Println("IsDir FileName : ", f.Name) if err := os.MkdirAll(tmp+f.Name, os.ModeDir); err != nil { log.Println("[E]", err) } } else { //fmt.Println("IsFile FileName : ", f.Name) rc, err := f.Open() if err != nil { log.Fatal(err) } defer rc.Close() fw, err := os.Create(tmp + f.Name) if err != nil { panic(err) } defer fw.Close() _, err = io.Copy(fw, rc) if err != nil { panic(err) } } } fmt.Println("unzip ok.") } func main() { //解析执行程序所在路径 file, _ := exec.LookPath(os.Args[0]) fmt.Println("file: " + file) exeFile := filepath.Base(file) fmt.Println("exeFile: " + exeFile) exePath, _ := filepath.Abs(file) fmt.Println("exePath: " + exePath) if len(exePath) > 1 { rs := []rune(exePath) exePath = string(rs[0:(len(exePath) - len(exeFile))]) } //读取配置文件并解析 config, err := readFile("conf.json") //fmt.Println("config: ", config) if err != nil { fmt.Println("Config: ", err.Error()) } //fmt.Println("Version: ", config.Version) //fmt.Println("UpdateTime: ", config.UpdateTime) fmt.Println("UpdateUrl: ", config.UpdateUrl) fmt.Println("ServiceName: ", config.ServiceName) fmt.Println("DeployPath: ", config.DeployPath) fmt.Println("DeployWar: ", config.DeployWar) fmt.Println("BackupPath: ", config.BackupPath) //读取网络更新配置文件 update, updateStr, err := httpGet(config.UpdateUrl) if err != nil { fmt.Printf("err was %v", err) } fmt.Println(update) fmt.Println("Version: " + update.Version) fmt.Println("UpdateTime: " + update.UpdateTime) fmt.Println("DownloadUrl: " + update.DownloadUrl) //校验版本是否需要更新 localUpdateFile := exePath + "update.json" localUpdate, err := localUpdateDetail(localUpdateFile) if err != nil { fmt.Println("Config: ", err.Error()) } if strings.EqualFold(localUpdate.UpdateTime, update.UpdateTime) { fmt.Println("No update package! Here is the last version.") } else { fmt.Println("Start update...") doDownload(config, update, exePath, updateStr, localUpdateFile) } } //func doBackup(w sync.WaitGroup, config ConfigDetail, name string) { // files_ch := make(chan *FileInfo, 100) // go WalkFiles(config.DeployPath+name, "", files_ch, w) // WriteFiles(config.BackupPath+name, files_ch) // fmt.Println("backup current files.") //} func doDownload(config ConfigDetail, update UpdateDetail, exePath, updateStr, localUpdateFile string) { //清除旧的部署备份 war := strings.Split(config.DeployWar, ".") var name string if len(war) > 0 { name = war[0] } //fmt.Println("name: ", name) //删除之前的备份文件 os.RemoveAll(config.BackupPath + name) os.RemoveAll(localUpdateFile) fmt.Println("delete old backup") //golang中的并发 sync和channel http://studygolang.com/articles/2027 //http://www.cnblogs.com/yjf512/archive/2013/01/30/2882570.html //http://ifeve.com/go-concurrent-waitgroup/ var w sync.WaitGroup w.Add(1) //备分部署环境 //go doBackup(w, config, name) files_ch := make(chan *FileInfo, 100) go WalkFiles(config.DeployPath+name, "", files_ch) go WriteFiles(config.BackupPath+name, files_ch, w) //下载更新包 fmt.Println("Start download...") updatePackageName, _, err := download(update.DownloadUrl, exePath) if err != nil { fmt.Println("Error in download: ", err.Error()) } else { fmt.Println("download ok. " + updatePackageName) w.Wait() doUpdate(config, update, name, exePath, updateStr, updatePackageName, localUpdateFile) } } func doUpdate(config ConfigDetail, update UpdateDetail, name, exePath, updateStr, updatePackageName, localUpdateFile string) { fmt.Println("updating...") //停止应用服务器 //stopService(config.ServiceName) //清理部署环境 for _, delContent := range update.DeleteContents { os.RemoveAll(config.DeployPath + name + "/" + delContent) fmt.Println("delContent: ", delContent) } //path := updatePackageName fmt.Println(updatePackageName) //解压缩下载文件 tmp := config.BackupPath + "tmp/" unzip(updatePackageName, tmp, config) //部署代码 var w sync.WaitGroup w.Add(1) files_tmp := make(chan *FileInfo, 100) go WalkFiles(tmp, "", files_tmp) //在一个独立的 goroutine 中遍历文件 go WriteFiles(config.DeployPath+name, files_tmp, w) w.Wait() //清理更新包下载临时解压文件 os.RemoveAll(tmp) fmt.Println("delete unzip temp path") //保存本地更新记录 reader := strings.NewReader(updateStr) fw, err := os.Create(localUpdateFile) if err != nil { panic(err) } defer fw.Close() _, err = io.Copy(fw, reader) if err != nil { panic(err) } fmt.Println("update ok.") //启动应用服务器 //startService(config.ServiceName) //fmt.Println("restart service ok.") } ```

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2283 次点击  
加入收藏 微博
2 回复  |  直到 2015-03-26 03:38:33
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传