Go的简单多核并行

117 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第14天,点击查看活动详情

我会继续沿用上一节GO并发操作使用的例子(GO并发实例 - 掘金 (juejin.cn)),还是员工制作咖啡,只不过现在我们假设有两个咖啡机(两个核),能够在同一时刻制作多份咖啡。如何将并发代码改为并行代码呢?

  1. 在代码改写的时候,我们如何识别咖啡机呢?肯定是增加名称字段,如下:
type CoffeeMachine struct {
	MachineName string
	CoffeeName  string
	Gopher
	Mlock sync.Mutex
}
  1. 对此,我们还需要增加一个初始化函数,也就是定义一个CoffeeMachine类型的数组,长度为2,然后对其初始化,如下:
var coffeeMachines = [2]CoffeeMachine{} //全局变量

func init() {
	coffeeMachines[0] = CoffeeMachine{MachineName: "Machine1"}
	coffeeMachines[1] = CoffeeMachine{MachineName: "Machine2"}
}
  1. 那到底哪个员工去用哪个咖啡机呢?我们可以指定员工ID为偶数的去0号机器,奇数的去1号机器。具体就是在MakeCoffee函数中根据GopherId选择数组中对应的machine。
coffeeMachine := &coffeeMachines[g.GopherId%2]
  1. 选择好machine之后就是打印信息,××员工在××机器上制作了××咖啡,之后就是取咖啡和喝咖啡,取咖啡的时候员工和咖啡机之间还是绑定的,所以需要改写参数列表
func (g *Gopher) TakeCoffee(coffeeMachine *CoffeeMachine)

TakeCoffee原来的参数列表为空,因为不需要,之前并发执行时,只有一台machine,不需要绑定,coffeeMachine对于TakeCoffee来说可以直接使用,但现在coffeeMachine有两种。

  1. 这次还改写了waitGroup,将wg.Done()写在函数里面了,而不是开一个匿名函数,写在函数调用的下一行。
func (g *Gopher) MakeCoffee(CoffeeName string, wg *sync.WaitGroup) {
	.......这是代码............
	wg.Done()
}

现在改写之后的所有代码: -multicore -coffeemachine.go -gopher.go -main.go

coffeemachine.go

package multicore

import "sync"

type CoffeeMachine struct {
	MachineName string
	CoffeeName  string
	Gopher
	Mlock sync.Mutex
}

gopher.go

package multicore

import (
	"fmt"
	"sync"
	"time"
)

type Gopher struct {
	GopherName string
	GopherId   int
	CoffeeName string
}

//实现MakeCoffee方法
//实现TakeCoffee方法
//实现DrinkCoffee方法

var coffeeMachines = [2]CoffeeMachine{} //全局变量

func init() {
	coffeeMachines[0] = CoffeeMachine{MachineName: "Machine1"}
	coffeeMachines[1] = CoffeeMachine{MachineName: "Machine2"}
}

func (g *Gopher) MakeCoffee(CoffeeName string, wg *sync.WaitGroup) {
	//实现MakeCoffee方法
	//没有coffee才能MakeCoffee

	coffeeMachine := &coffeeMachines[g.GopherId%2]
	coffeeMachine.Mlock.Lock()
	if coffeeMachine.CoffeeName == "" {
		coffeeMachine.CoffeeName = CoffeeName
		coffeeMachine.Gopher = *g
		fmt.Printf("%v is Making %v Coffee on Machine %v, his id is %v\n", g.GopherName, CoffeeName, coffeeMachine.MachineName, g.GopherId)
	}
	time.Sleep(5 * time.Second) //为了使得并行更加直观,我们在MakeCoffee睡眠5秒,其它go程也能在go程1完成makeCoffee之前另一个CoffeeMachine上makeCoffee
	g.TakeCoffee(coffeeMachine)
	coffeeMachine.Mlock.Unlock()
	g.DrinkCoffee()
	wg.Done()
}

func (g *Gopher) TakeCoffee(coffeeMachine *CoffeeMachine) {
	//实现TakeCoffee方法
	//有coffee才能TakeCoffee
	if coffeeMachine.CoffeeName != "" {
		g.CoffeeName = coffeeMachine.CoffeeName
		fmt.Printf("name=%v id=%v is taking %v Coffee\n", g.GopherId, g.GopherName, g.CoffeeName)
		coffeeMachine.CoffeeName = "" //取完coffee后,清空coffee
	} else {
		fmt.Printf("%v is Waiting for Coffee\n", g.GopherName)
	}
}

func (g *Gopher) DrinkCoffee() {
	//实现DrinkCoffee方法
	//有coffee才能DrinkCoffee
	if g.CoffeeName != "" {

		fmt.Printf("name=%v id=%v is Drinking %v Coffee\n", g.GopherId, g.GopherName, g.CoffeeName)
	} else {
		fmt.Printf("%v has no coffee to drink\n", g.GopherName)
	}
}

main.go

package main

import (
	"Concurrency/multicore"
	"runtime"
	"sync"
)

func main() {
	// runtime.GOMAXPROCS(1) // 1 CPU core
	// gopher1 := onecore.Gopher{GopherName: "gopher1", GopherId: 1}
	// gopher2 := onecore.Gopher{GopherName: "gopher2", GopherId: 2}

	// var c = make(chan struct{})
	// go func() {
	// 	gopher1.MakeCoffee("Latte")
	// 	gopher2.MakeCoffee("Luksusowa")
	// 	c <- struct{}{}
	// }()

	// <-c

	runtime.GOMAXPROCS(2) // 2 CPU cores
	gopher1 := multicore.Gopher{GopherName: "gopher1", GopherId: 1}
	gopher2 := multicore.Gopher{GopherName: "gopher2", GopherId: 2}
	gopher3 := multicore.Gopher{GopherName: "gopher3", GopherId: 3}
	gopher4 := multicore.Gopher{GopherName: "gopher4", GopherId: 4}
	var wg sync.WaitGroup
	wg.Add(4)

	go gopher1.MakeCoffee("Latte", &wg)
	go gopher2.MakeCoffee("Luksusowa", &wg)
	go gopher3.MakeCoffee("BlueMoutine", &wg)
	go gopher4.MakeCoffee("Luckin", &wg)
	wg.Wait()

}

代码运行结果为:

gopher4 is Making Luckin Coffee on Machine Machine1, his id is 4
gopher1 is Making Latte Coffee on Machine Machine2, his id is 1
name=1 id=gopher1 is taking Latte Coffee
name=1 id=gopher1 is Drinking Latte Coffee
name=4 id=gopher4 is taking Luckin Coffee
gopher3 is Making BlueMoutine Coffee on Machine Machine2, his id is 3
name=4 id=gopher4 is Drinking Luckin Coffee
gopher2 is Making Luksusowa Coffee on Machine Machine1, his id is 2  
name=3 id=gopher3 is taking BlueMoutine Coffee
name=3 id=gopher3 is Drinking BlueMoutine Coffee
name=2 id=gopher2 is taking Luksusowa Coffee    
name=2 id=gopher2 is Drinking Luksusowa Coffee

可以看到,Luckin咖啡还没被取出后,Latte就在另外一条咖啡机上制作,所以我们实现了并发。并行时所有咖啡只能等待前面的咖啡被取出后才能Make。