Go关键字和Channel实现并行

go关键字是实现并发的关键

go关键字用来创建goroutine(携程), 是实现并发的关键。go关键字用法如下

// go关键字放在方法调用前新建一个 goroutine  让他执行方法体
go GetResult(param1, param2);

//上例的变种,新建一个匿名方法并执行
go func(param1, param2){
}(val, val2)

// 直接新建一个goroutine   并在goroutine 中执行代码块
go {
  //do something
}

使用channel拿到并行的结果

//resultChan 是一个 int 类型的 channel。类似一个信封,里面放的是 int 类型的值。
var resultChan chan int
//将 123 放到这个信封里面,供别人从信封中取用
resultChan <- 123
//从 resultChan 中取值。这个时候 result := 123
result := <- resultChan

使用go关键字和channel实现非阻塞调用

阻塞的意思是调用方在被调用的代码返回之前必须一直等待,不能处理别的事情。而非阻塞调用则不用等待,调用之后立刻返回。那么返回值如何获取呢?Node.js 使用的是回调的方式,Golang 使用的是 channel。

/**
 * 每次调用方法会新建一个 channel : resultChan,
 * 同时新建一个 goroutine 来发起 http 请求并获取结果。
 * 获取到结果之后 goroutine 会将结果写入到 resultChan。
 */
func UnblockGet(requestUrl string) chan string {
    resultChan := make(chan string)
    go func() {
        request := httplib.Get(requestUrl)
        content, err := request.String()
        if err != nil {
            content = "" + err.Error()
        }
        resultChan <- content
    } ()
    return resultChan
}

由于新建的 goroutine 不会阻塞函数主流程的执行,所以调用 UnblockGet 方法会立刻得到一个 resultChan 返回值。一旦 goroutine 执行完毕拿到结果就会写入到 resultChan 中,这时外部就可以从 resultChan 中获取执行结果。

示例方法

type RemoteResult struct {
    Url string
    Result string
}

func RemoteGet(requestUrl string, resultChan chan RemoteResult)  {
    request := httplib.NewBeegoRequest(requestUrl, "GET")
    request.SetTimeout(2 * time.Second, 5 * time.Second)
    //request.String()
    content, err := request.String()
    if err != nil {
        content = "" + err.Error()
    }
    resultChan <- RemoteResult{Url:requestUrl, Result:content}
}
func MultiGet(urls []string) []RemoteResult {
    fmt.Println(time.Now())
    resultChan := make(chan RemoteResult, len(urls))
    defer close(resultChan)
    var result []RemoteResult
    //fmt.Println(result)
    for _, url := range urls {
        go RemoteGet(url, resultChan)
    }
    for i:= 0; i < len(urls); i++ {
        res := <-resultChan
        result = append(result, res)
    }
    fmt.Println(time.Now())
    return result
}

func main() {
    urls := []string{
        "http://127.0.0.1/test.php?i=13",
        "http://127.0.0.1/test.php?i=14",
        "http://127.0.0.1/test.php?i=15",
        "http://127.0.0.1/test.php?i=16",
        "http://127.0.0.1/test.php?i=17",
        "http://127.0.0.1/test.php?i=18",
        "http://127.0.0.1/test.php?i=19",
        "http://127.0.0.1/test.php?i=20"    }
    content := MultiGet(urls)
    fmt.Println(content)
}

示例方法二

func main(){
	// 创建一个无缓存的channel 数据类型为字符串
	c := make(chan string)
	//声明一个数组
	endList := [5]string{"first", "second", "third", "forty", "fifteen"}  
	for _,v := range endList{
		// 用a接受v的值
		go func(a string)  {    	
			time.Sleep(5 * time.Second)
			c <- a
			fmt.Println("add success")
		//把v作为参数传入到协程中
		}(v)           						
	}

	// 主进程一直监听处理channel, 当channel处理长度符合要求时,跳出循环语句
	var count int
	for data := range c{
		fmt.Println(data)
		count += 1
		if count == len(endList){
			break
		}
	}
}

参考文档

思否文章

posted @ 2020-06-03 10:51  _墨染丶  阅读(280)  评论(0编辑  收藏  举报