Is there some elegant way to pause and resume any other goroutine?

后端 未结 2 2019
傲寒
傲寒 2021-01-31 21:06

In my case, I have thousands of goroutines working simultaneously as work(). I also had a sync() goroutine. When sync starts, I need any o

相关标签:
2条回答
  • 2021-01-31 22:00

    I edited jimt's excellent example to not loop around when it reaches default case and if state == Paused.

     65         default:
     66             fmt.Printf("Default case\n")
     67             if state == Paused {
     68                 fmt.Printf("Default state Paused\n")
     69                 select {
     70                     case state = <-ws:
     71                         switch state {
     72                         case Running:
     73                             fmt.Printf("Worker %d: Running\n", id)
     74                             break
     75                         case Stopped:
     76                             fmt.Printf("Worker %d: Stopped\n", id)
     77                             return
     78                         }
     79                     break
     80                 }
     81             }
     82             fmt.Printf("Do actual work here\n")
    

    An extra select+case, combination will be required to avoid a forever loop.

    0 讨论(0)
  • 2021-01-31 22:11

    If I understand you correctly, you want N number of workers and one controller, which can pause, resume and stop the workers at will. The following code will do just that.

    package main
    
    import (
        "fmt"
        "runtime"
        "sync"
    )
    
    // Possible worker states.
    const (
        Stopped = 0
        Paused  = 1
        Running = 2
    )
    
    // Maximum number of workers.
    const WorkerCount = 1000
    
    func main() {
        // Launch workers.
        var wg sync.WaitGroup
        wg.Add(WorkerCount + 1)
    
        workers := make([]chan int, WorkerCount)
        for i := range workers {
            workers[i] = make(chan int, 1)
    
            go func(i int) {
                worker(i, workers[i])
                wg.Done()
            }(i)
        }
    
        // Launch controller routine.
        go func() {
            controller(workers)
            wg.Done()
        }()
    
        // Wait for all goroutines to finish.
        wg.Wait()
    }
    
    func worker(id int, ws <-chan int) {
        state := Paused // Begin in the paused state.
    
        for {
            select {
            case state = <-ws:
                switch state {
                case Stopped:
                    fmt.Printf("Worker %d: Stopped\n", id)
                    return
                case Running:
                    fmt.Printf("Worker %d: Running\n", id)
                case Paused:
                    fmt.Printf("Worker %d: Paused\n", id)
                }
    
            default:
                // We use runtime.Gosched() to prevent a deadlock in this case.
                // It will not be needed of work is performed here which yields
                // to the scheduler.
                runtime.Gosched()
    
                if state == Paused {
                    break
                }
    
                // Do actual work here.
            }
        }
    }
    
    // controller handles the current state of all workers. They can be
    // instructed to be either running, paused or stopped entirely.
    func controller(workers []chan int) {
        // Start workers
        setState(workers, Running)
    
        // Pause workers.
        setState(workers, Paused)
    
        // Unpause workers.
        setState(workers, Running)
    
        // Shutdown workers.
        setState(workers, Stopped)
    }
    
    // setState changes the state of all given workers.
    func setState(workers []chan int, state int) {
        for _, w := range workers {
            w <- state
        }
    }
    
    0 讨论(0)
提交回复
热议问题