golang学习笔记之手写一个执行器

前端之家收集整理的这篇文章主要介绍了golang学习笔记之手写一个执行器前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

之前介绍过一个多协程的Parallelize,允许多个协程并发执行任务的函数,今天手写一个控制能力更强的的ruuner,初步实现单个协程处理,后续将继续改进为多协程处理:

  1. package runner
  2.  
  3. import (
  4. "errors"
  5. "os"
  6. "os/signal"
  7. "time"
  8. )
  9.  
  10. type Runner struct {
  11. // interrupt channel reports a signal from the
  12. // operating system.
  13. interrupt chan os.Signal
  14.  
  15. // complete channel reports that processing is done.
  16. complete chan error
  17.  
  18. // timeout reports that time has run out.
  19. timeout <-chan time.Time
  20.  
  21. // tasks holds a set of functions that are executed
  22. // synchronously in index order.
  23. tasks []func(int)
  24. }
  25.  
  26. // ErrTimeout is returned when a value is received on the timeout.
  27. var ErrTimeout = errors.New("received timeout")
  28.  
  29. // ErrInterrupt is returned when an event from the OS is received.
  30. var ErrInterrupt = errors.New("received interrupt")
  31.  
  32. // New returns a new ready-to-use Runner.
  33. func New(d time.Duration) *Runner {
  34. return &Runner{
  35. interrupt: make(chan os.Signal, 1),complete: make(chan error),timeout: time.After(d),}
  36. }
  37.  
  38.  
  39. // Add attaches tasks to the Runner. A task is a function that
  40. // takes an int ID.
  41. func (r *Runner) Add(tasks ...func(int)) {
  42. r.tasks = append(r.tasks,tasks...)
  43. }
  44.  
  45. // Start runs all tasks and monitors channel events.
  46. func (r *Runner) Start() error {
  47. // We want to receive all interrupt based signals.
  48. // Run the different tasks on a different goroutine.
  49. go func() {
  50. r.complete <- r.run()
  51. }()
  52. select {
  53. // Signaled when processing is done.
  54. case err := <-r.complete:
  55. return err
  56. // Signaled when we run out of time.
  57. case <-r.timeout:
  58. return ErrTimeout
  59. }
  60. }
  61.  
  62. // run executes each registered task.
  63. func (r *Runner) run() error {
  64. for id,task := range r.tasks {
  65. // Check for an interrupt signal from the OS.
  66. if r.gotInterrupt() {
  67. return ErrInterrupt
  68. }
  69. // Execute the registered task.
  70. task(id)
  71. }
  72.  
  73. return nil
  74. }
  75.  
  76. // gotInterrupt verifies if the interrupt signal has been issued.
  77. func (r *Runner) gotInterrupt() bool {
  78.  
  79. select {
  80. // Signaled when an interrupt event is sent.
  81. case <-r.interrupt:
  82. // Stop receiving any further signals.
  83. signal.Stop(r.interrupt)
  84. return true
  85. // Continue running as normal.
  86. default:
  87. return false
  88. }
  89. }

注解写的很详细,我简单介绍一下;Runner是一单协程的运行器,里面几个属性interrupt获取os的信号量,complete返回执行结果,timeout设置超时时间,如果超时结束运行,tasks是报错任务的。添加任务通过Add方法:将方法加入到切片中,Start方法启动任务,这里只启动一个协程,后期改进,run方式是具体执行,执行task函数gotInterrupt获取os的消息。
怎样使用呢?看下面:

  1. const timeout = 2*time.Second
  2. func main() {
  3. r := runner.New(timeout)
  4. r.Add(crateTask(),crateTask(),crateTask())
  5. if err := r.Start(); err != nil {
  6. switch err {
  7. case runner.ErrTimeout:
  8. fmt.Println("timeout error")
  9. os.Exit(1)
  10. case runner.ErrInterrupt:
  11. fmt.Println("interrupt error")
  12. os.Exit(2)
  13. }
  14.  
  15. }
  16. log.Println("end !!!!")
  17.  
  18.  
  19. }
  20.  
  21. func crateTask()func(int) {
  22. return func(id int) {
  23. fmt.Println("exec id :",id)
  24. time.Sleep(time.Duration(id)*time.Second)
  25. }
  26. }

通过改变timeout时间可以准确的控制任务执行时间,上面2秒的例子,保证每个任务都能运行,执行结果如下: exec id : 0 exec id : 1 exec id : 2 timeout error exit status 1 如果改成5s当然能保证执行完成任务: exec id : 0 exec id : 1 exec id : 2 2017/04/11 09:59:12 end !!!!

猜你在找的Go相关文章