高朗 Goroutines


高朗中的并发是功能彼此独立运行的能力。 Goroutine是可以同时运行的函数。 高朗提供Goroutines作为并发处理操作的一种方式。

新的goroutine由 声明。

要将函数作为goroutine运行,请调用以go语句为前缀的函数。这是示例代码块:

sum()     // A normal function call that executes sum 同步hronously 和 waits for completing it
go sum()  // A 走 routine that executes sum asynchronously 和 doesn't wait for completing it

关键字使函数调用立即返回,而函数作为goroutine开始在后台运行,而程序的其余部分继续执行。的 主要 每个Golang程序的功能都使用goroutine启动,因此每个Golang程序至少运行一个goroutine。


创建goroutines

添加了 每次调用函数之前的关键字 responseSize。他们三个 responseSize 走 routines同时启动,并且三个调用 http.Get 同时进行。该程序不会等到一个响应返回后才发出下一个请求。结果,使用goroutines可以更快地打印出三种响应大小。

package 主要

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

func responseSize(url string) {
	fmt.Println("Step1: ", url)
	response, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Step2: ", url)
	defer response.Body.Close()

	fmt.Println("Step3: ", url)
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Step4: ", len(body))
}

func 主要() {
	go responseSize("//www.yfmac.net")
	go responseSize("//coderwall.com")
	go responseSize("//stackoverflow.com")
	time.Sleep(10 * time.Second)
}

我们添加了一个呼叫 时间睡眠 在里面 主要 该功能可防止主goroutine在 responseSize 走 routine可以完成。调用time.Sleep(10 * time.Second)将使 主要 走 routine睡觉10秒钟。

运行上面的程序时,您可能会看到以下输出:

C:\Golang\goroutines\create-simple-goroutine>走  run 主要.go
Step1:  //www.golangprograms.com
Step1:  //stackoverflow.com
Step1:  //coderwall.com
Step2:  //stackoverflow.com
Step3:  //stackoverflow.com
Step4:  116749
Step2:  //www.golangprograms.com
Step3:  //www.golangprograms.com
Step4:  79551
Step2:  //coderwall.com
Step3:  //coderwall.com
Step4:  203842


等待Goroutine完成执行

的WaitGroup类型 同步 软件包,用于等待程序完成从主函数启动的所有goroutine。它使用一个指定goroutine数量的计数器,并且Wait阻止程序执行,直到WaitGroup计数器为零。

方法用于将计数器添加到WaitGroup。

完成了 使用defer语句来调度WaitGroup方法以减少WaitGroup计数器。

等待 等待Group类型的方法等待程序完成所有goroutine。

在主函数内部调用Wait方法,该函数将阻止执行,直到WaitGroup计数器的值为零为止,并确保所有goroutine都已执行。

package 主要

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"同步"
)

// 等待Group is used to wait for the program to finish 走 routines.
var wg 同步.WaitGroup

func responseSize(url string) {
	// Schedule the call to 等待Group's 完成了 to tell 走 routine is completed.
	defer wg.Done()

	fmt.Println("Step1: ", url)
	response, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Step2: ", url)
	defer response.Body.Close()

	fmt.Println("Step3: ", url)
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Step4: ", len(body))
}

func 主要() {
	// 加 a count of three, one for each 走 routine.
	wg.Add(3)
	fmt.Println("Start Goroutines")

	go responseSize("//www.yfmac.net")
	go responseSize("//stackoverflow.com")
	go responseSize("//coderwall.com")

	// 等待 for the 走 routines to finish.
	wg.Wait()
	fmt.Println("Terminating Program")
}

运行上面的程序时,您可能会看到以下输出:


C:\Golang\goroutines\create-simple-goroutine>走  run 主要.go
Start Goroutines
Step1:  //coderwall.com
Step1:  //www.golangprograms.com
Step1:  //stackoverflow.com
Step2:  //stackoverflow.com
Step3:  //stackoverflow.com
Step4:  116749
Step2:  //www.golangprograms.com
Step3:  //www.golangprograms.com
Step4:  79801
Step2:  //coderwall.com
Step3:  //coderwall.com
Step4:  203842
Terminating Program

从Goroutines中获取值

从goroutine中获取值的最自然的方法是通道。通道是连接并发goroutine的管道。您可以将值从一个goroutine发送到通道,然后将这些值接收到另一个goroutine或在同步函数中。

package 主要

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"同步"
)

// 等待Group is used to wait for the program to finish 走 routines.
var wg 同步.WaitGroup

func responseSize(url string, nums chan int) {
	// Schedule the call to 等待Group's 完成了 to tell 走 routine is completed.
	defer wg.Done()

	response, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}
	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal(err)
	}
	// Send value to the unbuffered 渠道
	nums <- len(body)
}

func 主要() {
	nums := make(chan int) // Declare a unbuffered 渠道
	wg.Add(1)
	go responseSize("//www.yfmac.net", nums)
	fmt.Println(<-nums) // Read the value from unbuffered 渠道
	wg.Wait()
	close(nums) // Closes the 渠道
}

运行上面的程序时,您可能会看到以下输出:


C:\Golang\goroutines\create-simple-goroutine>走  run 主要.go
79655

播放和暂停Goroutine的执行

使用通道,我们可以播放和暂停执行goroutine。一种 渠道 通过充当goroutine之间的管道来处理此通信。

package 主要

import (
	"fmt"
	"同步"
	"time"
)

var i int

func work() {
	time.Sleep(250 * time.Millisecond)
	i++
	fmt.Println(i)
}

func routine(command <-chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	var status = "Play"
	for {
		select {
		case cmd := <-command:
			fmt.Println(cmd)
			switch cmd {
			case "Stop":
				return
			case "Pause":
				status = "Pause"
			default:
				status = "Play"
			}
		default:
			if status == "Play" {
				work()
			}
		}
	}
}

func 主要() {
	var wg 同步.WaitGroup
	wg.Add(1)
	command := make(chan string)
	go routine(command, &wg)

	time.Sleep(1 * time.Second)
	command <- "Pause"

	time.Sleep(1 * time.Second)
	command <- "Play"

	time.Sleep(1 * time.Second)
	command <- "Stop"

	wg.Wait()
}

运行上面的程序时,您可以看到以下输出:

1
2
3
4
Pause
Play
5
6
7
8
9
Stop

使用原子函数修复竞争条件

争用条件是由于对共享资源的不同步访问而引起的,并试图同时读取和写入该资源。

原子函数提供了用于同步访问整数和指针的低级锁定机制。原子功能通常用于修复竞争条件。

中的功能 原子同步 软件包通过锁定对共享资源的访问来提供支持同步goroutine的支持。

package 主要

import (
	"fmt"
	"runtime"
	"同步"
	"同步/atomic"
)

var (
	counter int32          // counter is a variable incremented by all 走 routines.
	wg      同步.WaitGroup // wg is used to wait for the program to finish.
)

func 主要() {
	wg.Add(3) // 加 a count of two, one for each 走 routine.

	go increment("Python")
	go increment("Java")
	go increment("高朗")

	wg.Wait() // 等待 for the 走 routines to finish.
	fmt.Println("Counter:", counter)

}

func increment(name string) {
	defer wg.Done() // Schedule the call to 完成了 to tell 主要 we are done.

	for range name {
		atomic.AddInt32(&counter,1)
		runtime.Gosched() // Yield the thread 和 be placed back in queue.
	}
}

加Int32 通过强制每次只能执行一个goroutine并完成一次加法操作,atomic软件包中的function可以同步整数值的加法。当goroutine尝试调用任何原子函数时,它们会自动与所引用的变量同步。

运行上面的程序时,您可以看到以下输出:

C:\Golang\goroutines>走  run -race 主要.go
Counter: 15

C:\Golang\goroutines>

请注意,如果您替换代码行 原子.AddInt32(&counter,1)计数器++,那么您将看到以下输出-

C:\Golang\goroutines>走  run -race 主要.go
==================
WARNING: DATA RACE
Read at 0x0000006072b0 by 走 routine 7:
  主要.increment()
      C:/Golang/goroutines/main.go:31 +0x76

Previous write at 0x0000006072b0 by 走 routine 8:
  主要.increment()
      C:/Golang/goroutines/main.go:31 +0x90

Goroutine 7 (running) created at:
  主要.main()
      C:/Golang/goroutines/main.go:18 +0x7e

Goroutine 8 (running) created at:
  主要.main()
      C:/Golang/goroutines/main.go:19 +0x96
==================
Counter: 15
Found 1 data race(s)
exit status 66

C:\Golang\goroutines>

使用Mutex定义关键部分

互斥锁用于在代码周围创建关键部分,以确保一次只能有一个goroutine可以执行该代码部分。

package 主要

import (
	"fmt"
	"同步"
)

var (
	counter int32          // counter is a variable incremented by all 走 routines.
	wg      同步.WaitGroup // wg is used to wait for the program to finish.
	mutex   同步.Mutex     // mutex is used to define a critical section of code.
)

func 主要() {
	wg.Add(3) // 加 a count of two, one for each 走 routine.

	go increment("Python")
	go increment("Go Programming Language")
	go increment("Java")

	wg.Wait() // 等待 for the 走 routines to finish.
	fmt.Println("Counter:", counter)

}

func increment(lang string) {
	defer wg.Done() // Schedule the call to 完成了 to tell 主要 we are done.

	for i := 0; i < 3; i++ {
		mutex.Lock()
		{
			fmt.Println(lang)
			counter++
		}
		mutex.Unlock()
	}
}

呼叫所定义的关键部分 锁()开锁() 保护针对计数器变量和读取名称变量文本的操作。运行上面的程序时,您可以看到以下输出:


C:\Golang\goroutines>走  run -race 主要.go
PHP stands for Hypertext Preprocessor.
PHP stands for Hypertext Preprocessor.
The Go Programming Language, also commonly referred to as 高朗
The Go Programming Language, also commonly referred to as 高朗
Counter: 4

C:\Golang\goroutines>