7. 并发模式(Go Tutorial)
来源:互联网 发布:漫画控不能连接网络 编辑:程序博客网 时间:2024/06/01 10:08
并发模式
- 1 runner
- 2 pool
- 3 work
在本章我们将学习如何使用包来简化并发程序的编写,以及为什么能简化的原因
7.1 runner
runner 包用于展示如何使用通道来监视程序的执行时间,如果程序的运行时间太长,也可以用 runner 包来终止程序。
当开发需要调度后台处理任务的程序的时候,这种模式会很有用。
runner 包源码
// Example is provided with help by Gabriel Aszalos.// Package runner manages the running and lifetime of a process.package runnerimport ( "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.type Runner struct { // interrupt channel reports a signal from the // operating system. 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 []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: 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.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.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. 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 }}
使用示例
// This sample program demonstrates how to use a channel to// monitor the amount of time the program is running and terminate// the program if it runs too long.package mainimport ( "log" "os" "time" "github.com/goinaction/code/chapter7/patterns/runner")// timeout is the number of second the program has to finish.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 run. r := runner.New(timeout) // Add the tasks to be run. r.Add(createTask(), createTask(), createTask()) // 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) { return func(id int) { log.Printf("Processor - Task #%d.", id) time.Sleep(time.Duration(id) * time.Second) }}
7.2 pool
这个包用于展示如何使用有缓冲的通道来实现资源池,来管理可以在任意数量的 goroutine 之间共享及独立使用的资源。
这种模式在需要共享一组静态资源的情况下非常有用。如果 goroutine 需要从池里得到这些资源中的一个,它们可以从池里申请,使用完后归还到资源池。
pool 包
// Example provided with help from Fatih Arslan and Gabriel Aszalos.// Package pool manages a user defined set of resources.package poolimport ( "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() }}
示例
// This sample program demonstrates how to use the pool package// to share a simulated set of database connections.package mainimport ( "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)}
7.3 work
work 包的目的是如何使用无缓存通道创建一个 goroutine 池。它允许使用者知道什么时候 goroutine 池正在工作,而且如果池里的所有 goroutine 都忙,无法接受新的工作的时候,也能及时通过通道通知调用者。
使用无缓冲通道能确保不会有工作在队列里丢失或者卡住,所有的工作都会被处理。
work 包
// Example provided with help from Jason Waldrip.// Package work manages a pool of goroutines to perform work.package workimport "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: make(chan Worker), } p.wg.Add(maxGoroutines) for i := 0; i < maxGoroutines; i++ { go func() { for w := range p.work { w.Task() } p.wg.Done() }() } return &p}// Run submits work to the pool.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()}
示例
// This sample program demonstrates how to use the work package// to use a pool of goroutines to get work done.package mainimport ( "log" "sync" "time" "github.com/goinaction/code/chapter7/patterns/work")// names provides a set of names to display.var names = []string{ "steve", "bob", "mary", "therese", "jason",}// 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()}
阅读全文
0 0
- 7. 并发模式(Go Tutorial)
- 6. 并发(Go Tutorial)
- Go worker并发模式
- go语言并发编程-----Goroutines 并发模式
- 1. Hello Go(Go Tutorial)
- go-database-sql-tutorial
- Go 并发
- go并发
- go-并发
- Go并发
- 8. 标准库(Go Tutorial)
- GO语言的并发
- Go-简洁的并发
- go语言并发
- GO语言学习-并发
- go并发学习
- Go-简洁的并发
- go 语言并发
- 服务器端设计的思想、方法及应用
- Java微信支付全教程demo【公众号支付】
- live555 调优总结
- turtlebot3_Python脚本控制运行
- C++ 获取exe路径
- 7. 并发模式(Go Tutorial)
- 架构architecture篇-----1.docker命令
- DirectShow学习-概述
- 谷歌Cartographer学习(1)-快速安装测试
- android开发如何集成支付宝sdk
- java.lang.NoClassDefFoundError: org.greenrobot.greendao.database.DatabaseOpenHelper$EncryptedHelper
- CentOS 7下安装MPICH3.2过程详解
- 基于2D的人脸跟踪显示
- excel导入去重复