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()}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大腿面肌肉疼怎么办 跑步后大腿酸疼怎么办? 运动后大腿胀痛怎么办 尔雅课程没看完怎么办 国家课程校本化怎么办 上海买房限购怎么办 投资公司要求回购股份怎么办 超过应届生落户年龄怎么办 造价员章到期了怎么办 造价员继续教育过期了怎么办 sd卡存储不够怎么办? 进项税大于销项税怎么办 公司扣员工奖金怎么办 墙面大面积有霉怎么办 电脑总是断网怎么办 造价师资格证书丢了怎么办 行测做题速度太慢怎么办 普通话准考证丢了怎么办 信用卡还款逾期了怎么办 淮北建洗煤厂需要怎么办 建设工程不结算怎么办 考监理没有职称怎么办 公开招标两次招标失败怎么办 山东省植保员证怎么办 工伤没开病假单怎么办 邮储银行怎么办金卡 农机被交警查怎么办 广联达识别板筋负筋重叠怎么办 掌上品的钱怎么办 财务纠纷被起诉怎么办 牙齿补后疼痛怎么办 牙齿表面蛀了怎么办 虫牙全掉了怎么办 牙齿蛀光了怎么办 虫牙全部掉完了怎么办 不喜欢向人请教怎么办 单位不交公积金怎么办 电锯链条掉了怎么办 天津公积金怎么办外地转入 学籍档案涂抹了怎么办 气相点火失败怎么办