I wrote a program for downloading files in concurrent / parallel (GOMAXPROCS > 1) manner.This is my 2nd (non-toy) program written in Go.  Please point out areas for improvement:package mainimport (        errors        fmt        github.com/kennygrant/sanitize        github.com/nu7hatch/gouuid        github.com/op/go-logging        io        net/http        net/url        os        path        path/filepath        strconv        strings        time)type TransferMessage struct {        urlstr         string        content_length int64        transferred    int64        finished       bool        filename       string}const read_chunk = 65536func MaybeWarn(err error, format string, args ...interface{}) {        if err != nil {                format +=  (%s)                args = append(args, err)                log.Warning(format, args...)        }}func MaybeExitErr(err error, format string, args ...interface{}) {        if err != nil {                format +=  (%s)                args = append(args, err)                log.Error(format, args...)                os.Exit(1)        }}func GetContLen(resp *http.Response) (content_length int64) {        clen := resp.Header.Get(Content-Length)        content_length, err := strconv.ParseInt(clen, 10, 64)        if err != nil {                content_length = -1                MaybeWarn(err, Cannot read content length for url: %s, resp.Request.URL)        }        return}func GetUrlFilePath(urlstr string) string {        urlp, err := url.Parse(urlstr)        fname := sanitize.Name(urlp.Path)        if err != nil || fname ==  {                uid, err := uuid.NewV4()                MaybeExitErr(err, Cannot generate uid for filename, problem url: %s, error: %s, urlstr, err)                fname = uid.String()        }        cwd, _ := filepath.Abs(filepath.Dir(os.Args[0]))        return filepath.Join(cwd, fname)}func DownloadFile(urlstr string, c chan TransferMessage, resp *http.Response) {        content_length := GetContLen(resp)        fpath := GetUrlFilePath(urlstr)        fo, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0666)        MaybeExitErr(err, Problem opening file: %s, fpath)        defer fo.Close()        defer resp.Body.Close()        t := time.Now()        for readnum, total := int64(0), int64(0); ; readnum, err = io.CopyN(fo, resp.Body, read_chunk) {                total += readnum                msg := TransferMessage{content_length: content_length,                        urlstr:      urlstr,                        transferred: total,                        finished:    false,                        filename:    fpath}                switch err {                case nil:                        if time.Since(t).Seconds() > float64(0.2) {                                log.Debug(Sending msg %s, msg)                                c <- msg                                t = time.Now()                        }                case io.EOF:                        log.Debug(Finished, sending msg %s, msg)                        msg.finished = true                        c <- msg                        return                default:                        MaybeWarn(err, Problem copying file %s, skipping URL %s, fpath, urlstr)                        msg.finished = true                        c <- msg                        return                }        }}func UrlRead(urlstr string, c chan TransferMessage) (start_success bool) {        hclient := new(http.Client)        req, err := http.NewRequest(GET, urlstr, nil)        MaybeWarn(err, Problem making request for URL %s : %s, skipping this URL, urlstr, req)        resp, err := hclient.Do(req)        MaybeWarn(err, Problem executing HTTP request for URL: %s, skipping this URL, urlstr)        if resp != nil {                switch resp.StatusCode {                case 200:                        go DownloadFile(urlstr, c, resp)                        return true                default:                        MaybeWarn(errors.New(resp.Status), Response status code: %s, skipping URL: %s, resp.StatusCode, urlstr)                }        }        return false}func SetupLog(level string) {        eff_level := logging.INFO        if strings.ToUpper(level) == DEBUG {                eff_level = logging.DEBUG        }        logging.SetLevel(eff_level, )}func ConsumeAllNonBlocking(c chan TransferMessage) (msg TransferMessage, read bool) {        for {                flag := false                select {                case msg := <-c:                        return msg, true                default:                        flag = true                }                if flag {                        break                }        }        return TransferMessage{}, false}func UpdateDownloadStatus(dchans *map[string]chan TransferMessage, dstatus *map[string]string) {        for urlstr, c := range *dchans {                last_msg, read_some := ConsumeAllNonBlocking(c)                log.Debug(last_msg %s read_some %s, last_msg, read_some)                if read_some {                        switch last_msg.content_length {                        case -1:                                (*dstatus)[urlstr] = ?                        case 0:                                (*dstatus)[urlstr] = 100%                        default:                                (*dstatus)[urlstr] = fmt.Sprintf(%d%%, last_msg.transferred*100/last_msg.content_length)                        }                        if last_msg.finished {                                (*dstatus)[urlstr] = 100%                        }                }        }}func PrintDownloadStatus(dchans *map[string]chan TransferMessage, dstatus *map[string]string) {        for urlstr, _ := range *dchans {                fmt.Printf(%s , (*dstatus)[urlstr])        }        fmt.Println()}func CheckAllValues(dstatus map[string]string, val string) bool {        for _, v := range dstatus {                if v != val {                        return false                }        }        return true}func WatchDownloads(dchans map[string]chan TransferMessage) {        dstatus := make(map[string]string)        for urlstr, _ := range dchans {                fmt.Printf(%s , path.Base(GetUrlFilePath(urlstr)))                dstatus[urlstr] = 0%        }        fmt.Println()        t1 := time.Now().Second()        for {                UpdateDownloadStatus(&dchans, &dstatus)                if CheckAllValues(dstatus, 100%) {                        fmt.Println(strings.Repeat(- , len(dstatus)))                        PrintDownloadStatus(&dchans, &dstatus)                        log.Debug(Completed %s, dstatus)                        break                }                t2 := time.Now().Second()                if t2-t1 == 1 {                        PrintDownloadStatus(&dchans, &dstatus)                        t1 = t2                }                time.Sleep(time.Duration(100) * time.Millisecond)        }}func DispatchDownloads(urls []string) {        dchans := make(map[string]chan TransferMessage)        for _, urlstr := range urls {                c := make(chan TransferMessage, 10)                if UrlRead(urlstr, c) {                        dchans[urlstr] = c                }        }        WatchDownloads(dchans)}var log = logging.MustGetLogger()func main() {        SetupLog(info)        DispatchDownloads(os.Args[1:])}
Concurrent download in Go
http;go;concurrency