goroutine和channel

一次只做一件事并不总是完成任务最快的方法。一些大问题可以分解成小任务。goroutine可以让程序同时处理几个不同的任务。goroutine可以使用channel来协调它们的工作,channel允许goroutine互相发送数据并同步,这样一个goroutine就不会领先于另一个goroutine。goroutine让你充分利用具有多处理器的计算机,让程序运行得尽可能快!

检索网页

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func getUrl(url string)  {
	get, err := http.Get(url)
	if err!=nil {
		recover()
		panic("get err")
	}
	if get==nil{
		recover()
		panic("body none 1")
	}
	body := get.Body
	f := func() {
		for body.Close()!=nil {
			fmt.Printf("Close get err")
		}
	}
	defer f()
	all, errR := ioutil.ReadAll(body)
	if errR != nil {
		recover()
		panic("body none 2")
	}
	fmt.Printf(string(all))
}
func main() {
	getUrl("http://www.baidu.com")
}

image-20201106103720223


多任务,使用goroutine的并发性

通过同时执行多个任务来找到加快程序运行速度的方法。

将大型任务分解为可以并发运行的较小子任务,有时可能意味着程序的速度会大大提高。

goroutine允许并发:暂停一个任务来处理其他任务。在某些情况下,它们允许并行:同时处理多个任务!

在Go中,并发任务称为goroutine。其他编程语言有一个类似的概念,叫作线程,但是goroutine比线程需要更少的计算机内存,启动和停止的时间更少,这意味着你可以同时运行更多的goroutine。

它们也更容易使用。要启动另一个goroutine,可以使用go语句,它只是一个普通的函数或方法调用,前面有go关键字:

image-20201106104042944


使用goroutine

没有使用

package main

import (
	"fmt"
	"time"
)
func main() {
	now := time.Now()
	for i:=0;i<30;i++ {
		func() {
			time.Sleep(100)
		}()
	}
	elapsed := time.Since(now)
	fmt.Println("\n该函数执行完成耗时:", elapsed)
}

image-20201106113239567


go语句不能使用返回值

切换到goroutine带来了另一个需要解决的问题:我们不能在go语句中使用函数返回值。

但是goroutine之间有一种交流方式:channel。channel不仅允许你将值从一个goroutine发送到另一个goroutine,还确保在接收的goroutine尝试使用该值之前,发送的goroutine已经发送了该值。

使用channel的唯一实际方法是从一个goroutine到另一个goroutine的通信。所以为了演示channel,我们需要做一些事情:

·创建一个channel。

·编写一个函数,该函数接收一个channel作为参数。我们将在一个单独的goroutine中运行这个函数,并使用它通过channel发送值。

每个channel只携带特定类型的值,因此可能有一个channel用于int值,另一个channel用于struct类型的值。要声明包含channel的变量,可以使用chan关键字,然后是channel将携带的值的类型。

image-20201106112235526

要实际创建channel,你需要调用内置的make函数(与创建映射和切片的函数相同)。传递make要创建的channel的类型(应该与要赋值给它的变量的类型相同)。

image-20201106112248548

不是单独声明channel变量,在大多数情况下,使用一个短变量声明更容易:

image-20201106112307895

使用channel发送和接收值

要在channel上发送值,可以使用<-运算符(这是一个小于号后面跟着一个英文破折号)。它看起来像一个箭头,从发送的值指向发送该值的channel。

image-20201106112424204

你还可以使用<-运算符来接收来自channel的值,但是位置不同:你将箭头放在接收channel的左侧。(这看起来有点像你从channel中取出一个值。)

image-20201106112434060

使用goroutine

package main

import (
	"fmt"
	"time"
)
var ints = make(chan int)
func main() {
	now := time.Now()
	for i:=0;i<30;i++ {
		go func() {
			time.Sleep(100)
			ints<-1
		}()
	}
	n:=0
	for ;n<29;n+=(<-ints) {
	}
	elapsed := time.Since(now)
	fmt.Println("\n该函数执行完成耗时:", elapsed)
}

image-20201106113330251

顺序执行

  • 我们提到,channel还确保发送的goroutine在接收channel尝试使用该值之前已经发送了该值。channel通过blocking(阻塞)——暂停当前goroutine中的所有进一步操作来实现这一点。发送操作阻塞发送goroutine,直到另一个goroutine在同一channel上执行了接收操作。反之亦然:接收操作阻塞接收goroutine,直到另一个goroutine在同一channel上执行了发送操作。这个行为允许goroutine同步它们的动作——协调它们的时间.
>package main

>import "time"

>var ints = make(chan int)
>func main() {
go func() {
	for i:=0;i<3;i++ {
		time.Sleep(time.Duration(1000000000))
		<-ints
		println("go ",i)
		ints<-1
	}
}()
for i:=0;i<3;i++ {
	ints<-1
	println("main   ",i)
	<-ints
}
>}

image-20201106153234175


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

nosql分类 上一篇
恢复 下一篇