29

Go语言:调度间隔用哪个好?time.Sleep v.s. time.After

 4 years ago
source link: https://www.tuicool.com/articles/BZJzQz7
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Go编程中循环调度任务的执行间隔我们通常采用 time.Sleep或time.After来实现。

写法1:

go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("Cancelled", time.Now())
				wg.Done()
				return
			default:
				time.Sleep(time.Second * 10)
				fmt.Println("Invoked", time.Now())
			}
		}
	}(ctx)

写法2:

go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("Cancelled", time.Now())
				wg.Done()
				return
			case <-time.After(time.Second * 10):
				fmt.Println("Invoked", time.Now())
			}
		}
	}(ctx)

这两种方式一样吗?那种方式更好呢? 以上两种写法,都可以满足需求。那么是否这两种写法是等效的呢? 当然不是,为了更好的说明它们的差别,请运行下面的这两段测试代码:

var wg sync.WaitGroup
func cancelTaskAfter(interval time.Duration, cancel context.CancelFunc) {
	go func(cancel context.CancelFunc) {
		time.Sleep(interval)
		fmt.Println("Cancell task", time.Now())
		cancel()
		wg.Done()
	}(cancel)
}

func TestTimerTask1(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	wg.Add(1)
	cancelTaskAfter(time.Second*25, cancel)
	wg.Add(1)
	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("Cancelled", time.Now())
				wg.Done()
				return
			default:
				time.Sleep(time.Second * 10)
				fmt.Println("Invoked", time.Now())
			}
		}
	}(ctx)
	wg.Wait()
}

func TestTimerTask2(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	wg.Add(1)
	cancelTaskAfter(time.Second*25, cancel)
	wg.Add(1)
	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("Cancelled", time.Now())
				wg.Done()
                               return
			case <-time.After(time.Second * 10):
				fmt.Println("Invoked", time.Now())
			}
		}
	}(ctx)
	wg.Wait()
}

也许看到这,你大概已经知道这两种写法的不同了。

以下是输出结果

=== RUN   TestTimerTask1
 Invoked 2019-09-22 17:12:07.055392 +0800 CST m=+35.007752073
 Invoked 2019-09-22 17:12:17.057073 +0800 CST m=+45.009375450
 Cancell task 2019-09-22 17:12:22.051311 +0800 CST m=+50.003583680
 Invoked 2019-09-22 17:12:27.060766 +0800 CST m=+55.013010382
 Cancelled 2019-09-22 17:12:27.060804 +0800 CST m=+55.013048342
 --- PASS: TestTimerTask1 (30.01s)
=== RUN   TestTimerTask2
Invoked 2019-09-22 17:11:42.049262 +0800 CST m=+10.001766181
Invoked 2019-09-22 17:11:52.053054 +0800 CST m=+20.005500478
Cancell task 2019-09-22 17:11:57.050145 +0800 CST m=+25.002563035
Cancelled 2019-09-22 17:11:57.05041 +0800 CST m=+25.002827648
--- PASS: TestTimerTask2 (25.00s)

采用Sleep来实现间隔的时候,如果cancel调用后任务协程正好处于sleep过程中,这时任务是无法被及时取消的。

小结 如果你需要任务能够被及时的取消,那你应该采用time.After来控制调用的间隔。

更多知识,欢迎订阅学习 《Go 语言入门到实战》

I73UN3e.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK