go语法--基础40--并发编程--案例3

一、生成器

类似于提供了一个服务
只是适用于调用不是很频繁的情况

1.1、代码

package main

import (
	"fmt"
	"math/rand"
)

//创建随机数的服务
func create_rand_server() chan int {
	out := make(chan int)
	go func() {
		for {
			num := rand.Int()
			fmt.Println("create_rand_server--->生成随机数num:", num)
			out <- num
		}
	}()
	return out
}

func main() {
	// 生成随机数作为一个服务
	rand_service_handler := create_rand_server()
	num := <-rand_service_handler
	fmt.Println("main--->num:", num)
}


输出
create_rand_server--->生成随机数num: 5577006791947779410
create_rand_server--->生成随机数num: 8674665223082153551
main--->num: 5577006791947779410

二、多路复用

多路复用技术可以用来整合多个通道。提升性能和操作的便捷。

其实就是整合了多个上面的生成器

2.1、代码

package main

import (
	"fmt"
	"math/rand"
)

//创建随机数的服务
func create_rand_server() chan int {
	out := make(chan int)
	go func() {
		for {
			num := rand.Int()
			fmt.Println("create_rand_server--->生成随机数num:", num)
			out <- num
		}
	}()
	return out
}

//多路复用的服务
func create_rand_server_multiple() chan int {
	create_rand_server1 := create_rand_server()
	create_rand_server2 := create_rand_server()
	out := make(chan int)

	go func() {
		for {
			//读取生成器1中的数据,整合
			num := <-create_rand_server1
			fmt.Println("create_rand_server_multiple--->create_rand_server1--->读取生成器1中的数据:", num)
			out <- num
		}
	}()
	go func() {
		for {
			//读取生成器2中的数据,整合
			num := <-create_rand_server2
			fmt.Println("create_rand_server_multiple--->create_rand_server2--->读取生成器2中的数据:", num)
			out <- num

		}
	}()
	return out
}

func main() {
	// 生成随机数作为一个服务
	rand_service_handler := create_rand_server_multiple()
	num := <-rand_service_handler
	fmt.Println("main--->num:", num)
}


输出

create_rand_server--->生成随机数num: 5577006791947779410
create_rand_server--->生成随机数num: 8674665223082153551
create_rand_server--->生成随机数num: 6129484611666145821
create_rand_server_multiple--->create_rand_server2--->读取生成器2中的数据: 8674665223082153551
main--->num: 8674665223082153551

三、Furture技术

  1. 可以在不准备好参数的情况下调用函数,函数调用和函数参数准备这两个过程可以完全解耦。

  2. 可以在调用的时候不关心数据是否准备好,返回值是否计算好的问题。让程序中的组件在准备好数据的时候自动跑起来。

  3. Furture技术可以和各个其他技术组合起来用。

    1. 可以通过多路复用技术,监听多个结果Channel,当有结果后,自动返回
    2. 可以和生成器组合使用,生成器不断生产数据,Furture技术逐个处理数据。
    3. Furture技术自身还可以首尾相连,形成一个并发的pipe filter。这个pipe filter可以用于读写数据流,操作数据流。

3.1、代码

package main

import (
	"fmt"
	"runtime"
)

type query struct {
	sql    chan string
	result chan string
}

//执行sql查询函数
func execQuery(q query) {
	go func() {
		sql := <-q.sql
		q.result <- "get " + sql
	}()

}

func main() {

	q := query{make(chan string, 1), make(chan string, 1)}

	//函数调用
	execQuery(q)
	//函数执行结果
	var result string
	//阻塞等待结果
	go func() {
		result = <-q.result
	}()

	//传入参数
	q.sql <- "select * from user"
	runtime.Gosched()
	fmt.Println("函数执行结果:", result)
}


输出
函数执行结果: get select * from user
 

四、Chain Filter技术

程序创建了5个Filter,每个分别过滤一个素数,可以输出前5个素数。

4.1、代码

package main

import "fmt"

//递增+1创建数字,并放入管道ch中
func Generate(ch chan<- int) {
	for i := 2; ; i++ {
		fmt.Println("Generate-->递增+1创建数字,并放入管道ch中:", i)
		ch <- i
	}
}

// 输出素数
func Filter(in <-chan int, out chan<- int, prime int, time int) {
	for {

		//读取in管道的数字
		i := <-in

		fmt.Println(time, "Filter--> i=", i, "-->prime=", prime)

		//如果是素数,输出到管道out
		if i%prime != 0 {
			fmt.Println(time, "Filter--> 如果是素数,输出到管道out:", i, "-->prime:", prime)
			out <- i // Send 'i' to 'out'.
		}
	}
}

// The prime sieve: Daisy-chain Filter processes.
func main() {
	//创建管道ch
	ch := make(chan int) // Create a new channel.
	//递增+1创建数字,并放入管道ch中
	// 并发执行
	go Generate(ch)
	// 读取前5个素数
	for i := 0; i < 5; i++ {
		//读取管道的值
		prime := <-ch
		fmt.Println("main-->素数:", prime)
		out := make(chan int)
		go Filter(ch, out, prime, i)
		ch = out
	}
}

输出

Generate-->递增+1创建数字,并放入管道ch中: 2
Generate-->递增+1创建数字,并放入管道ch中: 3
main-->素数: 2
0 Filter--> i= 3 -->prime= 2
0 Filter--> 如果是素数,输出到管道out: 3 -->prime: 2
main-->素数: 3
Generate-->递增+1创建数字,并放入管道ch中: 4
Generate-->递增+1创建数字,并放入管道ch中: 5
0 Filter--> i= 4 -->prime= 2
0 Filter--> i= 5 -->prime= 2
0 Filter--> 如果是素数,输出到管道out: 5 -->prime: 2
1 Filter--> i= 5 -->prime= 3
1 Filter--> 如果是素数,输出到管道out: 5 -->prime: 3
main-->素数: 5
Generate-->递增+1创建数字,并放入管道ch中: 6
Generate-->递增+1创建数字,并放入管道ch中: 7
0 Filter--> i= 6 -->prime= 2
0 Filter--> i= 7 -->prime= 2
0 Filter--> 如果是素数,输出到管道out: 7 -->prime: 2
1 Filter--> i= 7 -->prime= 3
1 Filter--> 如果是素数,输出到管道out: 7 -->prime: 3
2 Filter--> i= 7 -->prime= 5
2 Filter--> 如果是素数,输出到管道out: 7 -->prime: 5
main-->素数: 7
Generate-->递增+1创建数字,并放入管道ch中: 8
Generate-->递增+1创建数字,并放入管道ch中: 9
0 Filter--> i= 8 -->prime= 2
0 Filter--> i= 9 -->prime= 2
0 Filter--> 如果是素数,输出到管道out: 9 -->prime: 2
1 Filter--> i= 9 -->prime= 3
Generate-->递增+1创建数字,并放入管道ch中: 10
Generate-->递增+1创建数字,并放入管道ch中: 11
0 Filter--> i= 10 -->prime= 2
0 Filter--> i= 11 -->prime= 2
0 Filter--> 如果是素数,输出到管道out: 11 -->prime: 2
1 Filter--> i= 11 -->prime= 3
1 Filter--> 如果是素数,输出到管道out: 11 -->prime: 3
2 Filter--> i= 11 -->prime= 5
2 Filter--> 如果是素数,输出到管道out: 11 -->prime: 5
3 Filter--> i= 11 -->prime= 7
3 Filter--> 如果是素数,输出到管道out: 11 -->prime: 7
main-->素数: 11
Generate-->递增+1创建数字,并放入管道ch中: 12

Process finished with the exit code 0

五、共享变量

使用共享变量可以让代码更加简洁

5.1、代码

package main

import "fmt"

//设置共享变量的对象
type sharded_var struct {
	reader chan int
	writer chan int
}
//共享变量维护协程
func sharded_var_fun(v sharded_var) {
	go func() {
		var value int = 0
		for { //监听读写通道,完成服务
			select {
			//从管道writer中读取值
			case value = <-v.writer:
				//将值写入管道reader
			case v.reader <- value:
			}
		}
	}()
}

func main() {
	//初始化,并开始维护协程
	v := sharded_var{make(chan int), make(chan int)}
	sharded_var_fun(v)
	read:=<-v.reader
	fmt.Println("读取值",read)
	//写入值
	v.writer <- 1
	read=<-v.reader
	fmt.Println("再次读取值",read)
}


输出

读取值 0
再次读取值 1

六、常用并发模式–Runner

  1. 类似Java的Furture
  2. 将一个个任务,放到Furture中,任务串行执行

6.1、源码

// Example is provided with help by Gabriel Aszalos.
// Package runner manages the running and lifetime of a process.
package runner

import (
	"errors"
	"os"
	"os/signal"
	"time"
)

// Runner runs a set of tasks within a given timeout and can be
// shut down on an operating system interrupt.

// 定义了三个channel来通知任务状态.
 
type Runner struct {
	// interrupt channel reports a signal from the
	// operating system.
	//接收系统的终止信号(比如ctrl-c),接收到之后系统就优雅的退出
	interrupt chan os.Signal

	// complete channel reports that processing is done.
	// 指示任务完成状态或者返回错误
	complete chan error

	// timeout reports that time has run out.
	// 当超时了之后,系统就优雅的退出
	timeout <-chan time.Time

	// tasks holds a set of functions that are executed
	// synchronously in index order.
	// tasks是一个函数类型的slice,你可以往里面存放func funcName(id int){}的函数,作为你的任务
	tasks []func(int)
}

// ErrTimeout is returned when a value is received on the timeout channel.
var ErrTimeout = errors.New("received timeout")

// ErrInterrupt is returned when an event from the OS is received.
var ErrInterrupt = errors.New("received interrupt")

// New returns a new ready-to-use Runner.
func New(d time.Duration) *Runner {
	return &Runner{
		//interrupt channel buffer设置为1,也就是说当用户重复ctrl+c的时候,程序也只会收到一个信号,其他的信号会被丢弃.
		interrupt: make(chan os.Signal, 1),
		complete:  make(chan error),
		timeout:   time.After(d),
	}
}

// Add attaches tasks to the Runner. A task is a function that
// takes an int ID.
func (r *Runner) Add(tasks ...func(int)) {
	r.tasks = append(r.tasks, tasks...)
}

// Start runs all tasks and monitors channel events.
//在Start()方法中,在go块中执行run()方法,任何当前的goroutine会阻塞在select这边,直到收到run()返回的complete channel或者超时返回.
func (r *Runner) Start() error {
	// We want to receive all interrupt based signals.
	signal.Notify(r.interrupt, os.Interrupt)

	// Run the different tasks on a different goroutine.
	go func() {
		r.complete <- r.run()
	}()

	select {
	// Signaled when processing is done.
	case err := <-r.complete:
		return err

	// Signaled when we run out of time.
	case <-r.timeout:
		return ErrTimeout
	}
}

// run executes each registered task.
// 在run()方法中,在开始执行任务前(task(id)),会前检查执行流程有没有被中断(if r.gotInterrupt() {}),这里用了一个带default语句的select.一旦收到中断的事件,程序就不再接受任何其他事件了(signal.Stop(r.interrupt)).
func (r *Runner) run() error {
	for id, task := range r.tasks {
		// Check for an interrupt signal from the OS.
		if r.gotInterrupt() {
			return ErrInterrupt
		}

		// Execute the registered task.
		//tasks里面的任务是串行执行的,这些任务的执行发生在一个单独的goroutine中
		task(id)
	}

	return nil
}

// gotInterrupt verifies if the interrupt signal has been issued.
func (r *Runner) gotInterrupt() bool {
	select {
	// Signaled when an interrupt event is sent.
	case <-r.interrupt:
		// Stop receiving any further signals.
		signal.Stop(r.interrupt)
		return true

	// Continue running as normal.
	default:
		return false
	}
}

6.2、测试


package main

import (
	"log"
	"os"
	"time"

	"github.com/goinaction/code/chapter7/patterns/runner"
)

const timeout = 3 * time.Second

// main is the entry point for the program.
func main() {
	log.Println("Starting work.")

	// Create a new timer value for this runner.
	r := runner.New(timeout)

	// Add the tasks to be run.
	task1 := createTask()
	task2 := createTask()
	task3 := createTask()

	r.Add(task1, task2, task3)

	// Run the tasks and handle the result.
	if err := r.Start(); err != nil {
		switch err {
		case runner.ErrTimeout:
			log.Println("Terminating due to timeout.")
			os.Exit(1)
		case runner.ErrInterrupt:
			log.Println("Terminating due to interrupt.")
			os.Exit(2)
		}
	}

	log.Println("Process ended.")
}

// createTask returns an example task that sleeps for the specified
// number of seconds based on the id.
func createTask() func(int) {
	//id 是 数组的索引,具体看runner.run函数
	return func(id int) {
		log.Printf("Processor - Task #%d.", id)
		time.Sleep(time.Duration(id) * time.Second)
	}
}


输出

2021/06/26 17:19:17 Starting work.
2021/06/26 17:19:17 Processor - Task #0.
2021/06/26 17:19:17 Processor - Task #1.
2021/06/26 17:19:18 Processor - Task #2.
2021/06/26 17:19:20 Process ended.

七、常用并发模式–Pooling

类似Java的线程池

7.1、源码

// Example provided with help from Fatih Arslan and Gabriel Aszalos.
// Package pool manages a user defined set of resources.
package pool

import (
	"errors"
	"io"
	"log"
	"sync"
)

// Pool manages a set of resources that can be shared safely by
// multiple goroutines. The resource being managed must implement
// the io.Closer interface.
type Pool struct {
	m         sync.Mutex
	resources chan io.Closer
	factory   func() (io.Closer, error)
	closed    bool
}

// ErrPoolClosed is returned when an Acquire returns on a
// closed pool.
var ErrPoolClosed = errors.New("Pool has been closed.")

// New creates a pool that manages resources. A pool requires a
// function that can allocate a new resource and the size of
// the pool.
func New(fn func() (io.Closer, error), size uint) (*Pool, error) {
	if size <= 0 {
		return nil, errors.New("Size value too small.")
	}

	return &Pool{
		factory:   fn,
		resources: make(chan io.Closer, size),
	}, nil
}

// Acquire retrieves a resource	from the pool.
func (p *Pool) Acquire() (io.Closer, error) {
	select {
	// Check for a free resource.
	case r, ok := <-p.resources:
		log.Println("Acquire:", "Shared Resource")
		if !ok {
			return nil, ErrPoolClosed
		}
		return r, nil

	// Provide a new resource since there are none available.
	default:
		log.Println("Acquire:", "New Resource")
		return p.factory()
	}
}

// Release places a new resource onto the pool.
func (p *Pool) Release(r io.Closer) {
	// Secure this operation with the Close operation.
	p.m.Lock()
	defer p.m.Unlock()

	// If the pool is closed, discard the resource.
	if p.closed {
		r.Close()
		return
	}

	select {
	// Attempt to place the new resource on the queue.
	case p.resources <- r:
		log.Println("Release:", "In Queue")

	// If the queue is already at cap we close the resource.
	default:
		log.Println("Release:", "Closing")
		r.Close()
	}
}

// Close will shutdown the pool and close all existing resources.
func (p *Pool) Close() {
	// Secure this operation with the Release operation.
	p.m.Lock()
	defer p.m.Unlock()

	// If the pool is already close, don't do anything.
	if p.closed {
		return
	}

	// Set the pool as closed.
	p.closed = true

	// Close the channel before we drain the channel of its
	// resources. If we don't do this, we will have a deadlock.
	close(p.resources)

	// Close the resources
	for r := range p.resources {
		r.Close()
	}
}

7.2、测试

// This sample program demonstrates how to use the pool package
// to share a simulated set of database connections.
package main

import (
	"io"
	"log"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"

	"github.com/goinaction/code/chapter7/patterns/pool"
)

const (
	maxGoroutines   = 25 // the number of routines to use.
	pooledResources = 2  // number of resources in the pool
)

// dbConnection simulates a resource to share.
type dbConnection struct {
	ID int32
}

// Close implements the io.Closer interface so dbConnection
// can be managed by the pool. Close performs any resource
// release management.
func (dbConn *dbConnection) Close() error {
	log.Println("Close: Connection", dbConn.ID)
	return nil
}

// idCounter provides support for giving each connection a unique id.
var idCounter int32

// createConnection is a factory method that will be called by
// the pool when a new connection is needed.
func createConnection() (io.Closer, error) {
	id := atomic.AddInt32(&idCounter, 1)
	log.Println("Create: New Connection", id)

	return &dbConnection{id}, nil
}

// main is the entry point for all Go programs.
func main() {
	var wg sync.WaitGroup
	wg.Add(maxGoroutines)

	// Create the pool to manage our connections.
	p, err := pool.New(createConnection, pooledResources)
	if err != nil {
		log.Println(err)
	}

	// Perform queries using connections from the pool.
	for query := 0; query < maxGoroutines; query++ {
		// Each goroutine needs its own copy of the query
		// value else they will all be sharing the same query
		// variable.
		go func(q int) {
			performQueries(q, p)
			wg.Done()
		}(query)
	}

	// Wait for the goroutines to finish.
	wg.Wait()

	// Close the pool.
	log.Println("Shutdown Program.")
	p.Close()
}

// performQueries tests the resource pool of connections.
func performQueries(query int, p *pool.Pool) {
	// Acquire a connection from the pool.
	conn, err := p.Acquire()
	if err != nil {
		log.Println(err)
		return
	}

	// Release the connection back to the pool.
	defer p.Release(conn)

	// Wait to simulate a query response.
	time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
	log.Printf("Query: QID[%d] CID[%d]\n", query, conn.(*dbConnection).ID)
}


输出

021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 2
2021/06/26 21:53:29 Create: New Connection 1
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 3
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 4
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 5
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 7
2021/06/26 21:53:29 Create: New Connection 6
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 8
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 9
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 10
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 11
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 12
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 13
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 14
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 15
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 16
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 17
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 18
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 19
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 20
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 21
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 22
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 23
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 24
2021/06/26 21:53:29 Acquire: New Resource
2021/06/26 21:53:29 Create: New Connection 25
2021/06/26 21:53:29 Query: QID[23] CID[25]
2021/06/26 21:53:29 Release: In Queue
2021/06/26 21:53:29 Query: QID[8] CID[4]
2021/06/26 21:53:29 Release: In Queue
2021/06/26 21:53:29 Query: QID[2] CID[2]
2021/06/26 21:53:29 Release: Closing
2021/06/26 21:53:29 Close: Connection 2
2021/06/26 21:53:29 Query: QID[4] CID[5]
2021/06/26 21:53:29 Release: Closing
2021/06/26 21:53:29 Close: Connection 5
2021/06/26 21:53:29 Query: QID[13] CID[14]
2021/06/26 21:53:29 Release: Closing
2021/06/26 21:53:29 Close: Connection 14
2021/06/26 21:53:29 Query: QID[21] CID[20]
2021/06/26 21:53:29 Release: Closing
2021/06/26 21:53:29 Close: Connection 20
2021/06/26 21:53:30 Query: QID[14] CID[13]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 13
2021/06/26 21:53:30 Query: QID[17] CID[17]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 17
2021/06/26 21:53:30 Query: QID[18] CID[19]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 19
2021/06/26 21:53:30 Query: QID[3] CID[24]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 24
2021/06/26 21:53:30 Query: QID[15] CID[16]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 16
2021/06/26 21:53:30 Query: QID[9] CID[10]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 10
2021/06/26 21:53:30 Query: QID[6] CID[7]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 7
2021/06/26 21:53:30 Query: QID[5] CID[6]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 6
2021/06/26 21:53:30 Query: QID[19] CID[18]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 18
2021/06/26 21:53:30 Query: QID[12] CID[9]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 9
2021/06/26 21:53:30 Query: QID[20] CID[22]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 22
2021/06/26 21:53:30 Query: QID[24] CID[21]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 21
2021/06/26 21:53:30 Query: QID[11] CID[12]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 12
2021/06/26 21:53:30 Query: QID[22] CID[23]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 23
2021/06/26 21:53:30 Query: QID[7] CID[8]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 8
2021/06/26 21:53:30 Query: QID[10] CID[11]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 11
2021/06/26 21:53:30 Query: QID[16] CID[15]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 15
2021/06/26 21:53:30 Query: QID[1] CID[3]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 3
2021/06/26 21:53:30 Query: QID[0] CID[1]
2021/06/26 21:53:30 Release: Closing
2021/06/26 21:53:30 Close: Connection 1
2021/06/26 21:53:30 Shutdown Program.
2021/06/26 21:53:30 Close: Connection 25
2021/06/26 21:53:30 Close: Connection 4

八、Work

类似Java的固定线程池

8.1、源码


// Example provided with help from Jason Waldrip.
// Package work manages a pool of goroutines to perform work.
package work

import "sync"

// Worker must be implemented by types that want to use
// the work pool.
type Worker interface {
	Task()
}

// Pool provides a pool of goroutines that can execute any Worker
// tasks that are submitted.
type Pool struct {
	work chan Worker
	wg   sync.WaitGroup
}

// New creates a new work pool.
func New(maxGoroutines int) *Pool {
 
 
	p := Pool{
		//work是一个无缓存的 channel
		work: make(chan Worker),
	}
	//开启了固定个数(maxGoroutines)个goroutine
	p.wg.Add(maxGoroutines)
	for i := 0; i < maxGoroutines; i++ {
		go func() {
		// work是一个无缓存的 channel,这个for range会阻塞直到channel中有值可以取.要是work这个channel被关闭了,这个for range就结束,然后调用wg.Done
			for w := range p.work {
				w.Task()
			}
			p.wg.Done()
		}()
	}

	return &p
}

// Run submits work to the pool.
// 提交任务到pool中去,注意这个work是一个无缓存的 channel,所以得等一个goroutine把它取走,否则会阻塞住.这是我们需要保证的,
// 因为我们想要调用者保证这个任务被提交之后立即开始运行
func (p *Pool) Run(w Worker) {
	p.work <- w
}

// Shutdown waits for all the goroutines to shutdown.
func (p *Pool) Shutdown() {
	close(p.work)
	p.wg.Wait()
}

8.2、测试

// This sample program demonstrates how to use the pool package
// to share a simulated set of database connections.
package main

import (
	"log"
	"sync"
	"time"

	"github.com/goinaction/code/chapter7/patterns/work"
)

// names provides a set of names to display.
var names = []string{
	"李1",
	"李2",
	"李3",
	"李4",
	"李5",
}

// namePrinter provides special support for printing names.
type namePrinter struct {
	name string
}

// Task implements the Worker interface.
func (m *namePrinter) Task() {
	log.Println(m.name)
	time.Sleep(time.Second)
}

// main is the entry point for all Go programs.
func main() {
	// Create a work pool with 2 goroutines.
	p := work.New(2)

	var wg sync.WaitGroup
	wg.Add(100 * len(names))

	for i := 0; i < 100; i++ {
		// Iterate over the slice of names.
		for _, name := range names {
			// Create a namePrinter and provide the
			// specific name.
			np := namePrinter{
				name: name,
			}

			go func() {
				// Submit the task to be worked on. When RunTask
				// returns we know it is being handled.
				p.Run(&np)
				wg.Done()
			}()
		}
	}

	wg.Wait()

	// Shutdown the work pool and wait for all existing work
	// to be completed.
	p.Shutdown()
}


输出

2021/06/26 22:14:54 李1
2021/06/26 22:14:54 李2
2021/06/26 22:14:55 李4
2021/06/26 22:14:55 李4
2021/06/26 22:14:56 李1
2021/06/26 22:14:56 李5
2021/06/26 22:14:57 李3
2021/06/26 22:14:57 李2
2021/06/26 22:14:58 李3
2021/06/26 22:14:58 李5
2021/06/26 22:14:59 李1
2021/06/26 22:14:59 李2
2021/06/26 22:15:00 李5
2021/06/26 22:15:00 李4
2021/06/26 22:15:01 李1
2021/06/26 22:15:01 李2
2021/06/26 22:15:02 李3
2021/06/26 22:15:02 李3

Process finished with the exit code -1073741510 (0xC000013A: interrupted by Ctrl+C)


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值