1.延时的一般实现方式

1.1 time.Sleep

time.Sleep是一种简单的延时方法,它会让当前的协程暂停执行一段时间,等待一定的时间后再继续执行。然而,操作系统在调度任务时不一定会按照我们期望的那样精确地执行这个时间。此外,调用time.Sleep会导致当前的协程阻塞。

1.1.1 测试代码

//use time.Sleep delay delayus_nus
	for i := 0; i < tryCnt; i++ {
		startTime := time.Now()
		time.Sleep(time.Microsecond * delayus_n)
		fmt.Printf("use time.Sleep() delay %d us , real delay %v us\n",
                    delayus_n, (time.Now().Nanosecond()-startTime.Nanosecond())/1000)
	}

1.1.2 测试数据:

use time.Sleep() delay 10000 us , real delay 10336 us
use time.Sleep() delay 10000 us , real delay 10636 us
use time.Sleep() delay 10000 us , real delay 10565 us
use time.Sleep() delay 10000 us , real delay 10767 us
use time.Sleep() delay 10000 us , real delay 11318 us
use time.Sleep() delay 10000 us , real delay 10567 us
use time.Sleep() delay 10000 us , real delay 10233 us
use time.Sleep() delay 10000 us , real delay 10422 us
use time.Sleep() delay 10000 us , real delay 11870 us
use time.Sleep() delay 10000 us , real delay 11321 us
use time.Sleep() delay 10000 us , real delay 10244 us
use time.Sleep() delay 10000 us , real delay 10441 us
use time.Sleep() delay 10000 us , real delay 10660 us
use time.Sleep() delay 10000 us , real delay 10255 us
use time.Sleep() delay 10000 us , real delay 10438 us
use time.Sleep() delay 10000 us , real delay 10259 us
use time.Sleep() delay 10000 us , real delay 10296 us
use time.Sleep() delay 10000 us , real delay 10285 us
use time.Sleep() delay 10000 us , real delay 10236 us
use time.Sleep() delay 10000 us , real delay 10152 us

使用time.Sleep 延时10000us, 实际延时最大值为11870 us, 最小值为10152us。从测试数据看,延时10ms 误差在2ms以内,都为正误差。

1.2 time.After

time.After是一个非常常用的延时方法,它会在指定的时间后返回一个channel,并向这个channel中写入一个时间值。我们可以通过在这个channel上进行读取操作来实现延时效果。然而,time.After的实现依赖于time.NewTimer,而time.NewTimer的实现依赖于操作系统的计时器,所以也无法保证精确延时。

1.2.1 测试代码

	//use time.Afte delay delayus_nus
	for i := 0; i < tryCnt; i++ {
		afterTimer := time.Microsecond * delayus_n
		startTime := time.Now()
		timerChan := time.After(afterTimer)
		<-timerChan //block, wait timeout
		fmt.Printf("use time.After() delay %d us , real delay %v us\n",
			delayus_n, (time.Now().Nanosecond()-startTime.Nanosecond())/1000)
	}

1.2.2 测试结果

use time.After() delay 10000 us , real delay 10152 us
use time.After() delay 10000 us , real delay 10133 us
use time.After() delay 10000 us , real delay 10161 us
use time.After() delay 10000 us , real delay 10287 us
use time.After() delay 10000 us , real delay 11829 us
use time.After() delay 10000 us , real delay 10329 us
use time.After() delay 10000 us , real delay 10250 us
use time.After() delay 10000 us , real delay 10331 us
use time.After() delay 10000 us , real delay 10380 us
use time.After() delay 10000 us , real delay 10559 us
use time.After() delay 10000 us , real delay 10238 us
use time.After() delay 10000 us , real delay 10179 us
use time.After() delay 10000 us , real delay 11311 us
use time.After() delay 10000 us , real delay 10189 us
use time.After() delay 10000 us , real delay 10252 us
use time.After() delay 10000 us , real delay 10280 us
use time.After() delay 10000 us , real delay 10534 us
use time.After() delay 10000 us , real delay 10545 us
use time.After() delay 10000 us , real delay 10435 us
use time.After() delay 10000 us , real delay 10349 us

使用time.After 延时10000us, 实际延时最大值为11829 us, 最小值为10133us, 以上数据是在系统轻负载的情况下测试的,不同的系统负载及不同的系统时钟滴答会导致不一样的延时精确度。从测试数据看,轻载时延时10ms 误差在2ms以内,都为正误差。

1.3. time.AfterFunc

1.3.1 测试代码

//use time.Afte delay delayus_nus
	for i := 0; i < tryCnt; i++ {
		afterTimer := time.Microsecond * delayus_n
		startTime := time.Now()
		time.AfterFunc(afterTimer, func() {
			fmt.Printf("use time.AfterFunc() delay %d us , real delay %v us\n",
				delayus_n, (time.Now().Nanosecond()-startTime.Nanosecond())/1000)
		})
		time.Sleep(time.Millisecond * 100)
	}

1.3.2 测试数据

use time.AfterFunc() delay 10000 us , real delay 10633 us
use time.AfterFunc() delay 10000 us , real delay 13525 us
use time.AfterFunc() delay 10000 us , real delay 12950 us
use time.AfterFunc() delay 10000 us , real delay 11861 us
use time.AfterFunc() delay 10000 us , real delay 11059 us
use time.AfterFunc() delay 10000 us , real delay 10579 us
use time.AfterFunc() delay 10000 us , real delay 11977 us
use time.AfterFunc() delay 10000 us , real delay 12254 us
use time.AfterFunc() delay 10000 us , real delay 12480 us
use time.AfterFunc() delay 10000 us , real delay 11097 us
use time.AfterFunc() delay 10000 us , real delay 10911 us
use time.AfterFunc() delay 10000 us , real delay 10416 us
use time.AfterFunc() delay 10000 us , real delay 10742 us
use time.AfterFunc() delay 10000 us , real delay 10968 us
use time.AfterFunc() delay 10000 us , real delay 10194 us
use time.AfterFunc() delay 10000 us , real delay 13070 us
use time.AfterFunc() delay 10000 us , real delay 16510 us
use time.AfterFunc() delay 10000 us , real delay 10251 us
use time.AfterFunc() delay 10000 us , real delay 18563 us
use time.AfterFunc() delay 10000 us , real delay 10683 us

使用time.AfterFunc 延时10000us, 实际延时最大值为18563 us, 最小值为10251us。从测试数据看,轻载时延时10ms 误差在10ms以内,都为正误差, 延时误差比time.Sleep以及 time.After都大。

1.4 time.Tick

time.Tick是一个定时器,它会每隔一段时间向一个channel中发送一个时间值。这个定时器的实现也依赖于time.NewTicker,因此也无法保证精确延时。

1.4.1 测试代码

//use time.Tick
	interval := time.Microsecond * delayus_n
	startTime := time.Now()
	cycleTimerChan := time.Tick(interval)
	cnt := tryCnt
	for cnt > 0 {
		select {
		case <-cycleTimerChan:
			fmt.Printf("use time.Tick delay  %d us , real delay %v us\n",
				delayus_n, (time.Now().Nanosecond()-startTime.Nanosecond())/1000)
			startTime = time.Now()
			cnt--
		default:

		}
	}

1.4.2 测试数据

use time.Tick delay  10000 us , real delay 10676 us
use time.Tick delay  10000 us , real delay 11191 us
use time.Tick delay  10000 us , real delay 8178 us
use time.Tick delay  10000 us , real delay 10180 us
use time.Tick delay  10000 us , real delay 10461 us
use time.Tick delay  10000 us , real delay 9254 us
use time.Tick delay  10000 us , real delay 10182 us
use time.Tick delay  10000 us , real delay 10178 us
use time.Tick delay  10000 us , real delay 10175 us
use time.Tick delay  10000 us , real delay 11814 us
use time.Tick delay  10000 us , real delay 8662 us
use time.Tick delay  10000 us , real delay 9204 us
use time.Tick delay  10000 us , real delay 10181 us
use time.Tick delay  10000 us , real delay 10455 us
use time.Tick delay  10000 us , real delay 10166 us
use time.Tick delay  10000 us , real delay 9310 us
use time.Tick delay  10000 us , real delay 10145 us
use time.Tick delay  10000 us , real delay 10199 us
use time.Tick delay  10000 us , real delay 9311 us
use time.Tick delay  10000 us , real delay 10174 us

使用time.AfterFunc 实现10ms定时器, 实际延时最大值为11841 us, 最小值为8176us。 从测试数据看,轻载时延时10ms 误差在2ms以内,正负偏差都会出现, 延时误差与time.Sleep及 time.After相当,但会出现负偏差。

1.5 调用 linux usleep

1.5.1 测试代码

/*
#include <unistd.h>
void delayus(int delayus_n){
	usleep(delayus_n);
}
*/
//use c.sleep delay delayus_nus
	for i := 0; i < tryCnt; i++ {
		startTime := time.Now()
		C.delayus(C.int(delayus_n))
		fmt.Printf("use c.sleep delay %d us , real delay %v us\n",
			delayus_n, (time.Now().Nanosecond()-startTime.Nanosecond())/1000)
	}

1.5.1 测试数据

se c.sleep delay 10000 us , real delay 10415 us
use c.sleep delay 10000 us , real delay 10400 us
use c.sleep delay 10000 us , real delay 10611 us
use c.sleep delay 10000 us , real delay 10114 us
use c.sleep delay 10000 us , real delay 10096 us
use c.sleep delay 10000 us , real delay 10068 us
use c.sleep delay 10000 us , real delay 10171 us
use c.sleep delay 10000 us , real delay 10246 us
use c.sleep delay 10000 us , real delay 10110 us
use c.sleep delay 10000 us , real delay 10089 us
use c.sleep delay 10000 us , real delay 10192 us
use c.sleep delay 10000 us , real delay 10198 us
use c.sleep delay 10000 us , real delay 10352 us
use c.sleep delay 10000 us , real delay 10547 us
use c.sleep delay 10000 us , real delay 10679 us
use c.sleep delay 10000 us , real delay 10098 us
use c.sleep delay 10000 us , real delay 10105 us
use c.sleep delay 10000 us , real delay 10103 us
use c.sleep delay 10000 us , real delay 10099 us
use c.sleep delay 10000 us , real delay 10109 us

调用linux 系统延时函数usleep实现10ms延时, 实际延时最大值为10547 us, 最小值为10089us。从测试数据看,轻载时延时10ms 误差在1ms以内,均为正偏差, 延时比以上实现都要精确。

1.6 syscall.Nauosleep

1.6.1 测试代码

	//use syscall.syscall.Nanosleep
	for i := 0; i < tryCnt; i++ {
		startTime := time.Now()

		sleepDuration := time.Duration(time.Microsecond * delayus_n)
		sleepTime := syscall.NsecToTimespec(int64(sleepDuration))
		syscall.Nanosleep(&sleepTime, nil)
		fmt.Printf("use syscall.Nanosleep delay %d us , real delay %v us\n",
			delayus_n, (time.Now().Nanosecond()-startTime.Nanosecond())/1000)
	}

1.6.2 测试数据

use syscall.Nanosleep delay 10000 us , real delay 10179 us
use syscall.Nanosleep delay 10000 us , real delay 10207 us
use syscall.Nanosleep delay 10000 us , real delay 10254 us
use syscall.Nanosleep delay 10000 us , real delay 10194 us
use syscall.Nanosleep delay 10000 us , real delay 11284 us
use syscall.Nanosleep delay 10000 us , real delay 10154 us
use syscall.Nanosleep delay 10000 us , real delay 10144 us
use syscall.Nanosleep delay 10000 us , real delay 10460 us
use syscall.Nanosleep delay 10000 us , real delay 11650 us
use syscall.Nanosleep delay 10000 us , real delay 10116 us
use syscall.Nanosleep delay 10000 us , real delay 10242 us
use syscall.Nanosleep delay 10000 us , real delay 10051 us
use syscall.Nanosleep delay 10000 us , real delay 10163 us
use syscall.Nanosleep delay 10000 us , real delay 10108 us
use syscall.Nanosleep delay 10000 us , real delay 10186 us
use syscall.Nanosleep delay 10000 us , real delay 10132 us
use syscall.Nanosleep delay 10000 us , real delay 10101 us
use syscall.Nanosleep delay 10000 us , real delay 10137 us
use syscall.Nanosleep delay 10000 us , real delay 10778 us
use syscall.Nanosleep delay 10000 us , real delay 10099 us

系统调用syscall.Nanosleep实现10ms延时, 实际延时最大值为11284 us, 最小值为10051。从测试数据看, 此方法与c.usleep精确度相当。

以上数据是在系统轻负载的情况下测试的,不同的系统负载及不同的系统时钟滴答会导致不一样的延时精确度。


未完待续