前言

在实际项目开发中,经常会有定时任务的功能开发需求,定时任务主要分为两种,

1,在固定的时刻执行某个任务,也就是 Timer
2,基于固定的时间间隔,周期的执行某个任务,也就是Ticker

很多基于时间的调度任务框架都离不开这两种类型。

本文将会分别介绍在Golang和Rust语言中这两种定时器类型以及它们的使用方法。

Golang

Golang的标准库 time 中就包含了 Ticker 和 Timer 这两种定时器类型,在package中添加引用就可,如下: ​

import ("time"
)

Rust

本文中,对于Rust 将会使用第三方crate crossbeam-channel 提供的定时器类型,因为这个crate中的特性和Golang的特性是非常类似的,两者之间才具有可比性。

https://docs.rs/crossbeam/0.8.1/crossbeam/channel/fn.tick.html
https://docs.rs/crossbeam/0.8.1/crossbeam/channel/fn.after.html

因为是第三方create,所以在Cargo.toml中添加下面内容

crossbeam = "0.8"
crossbeam-channel = "0.5"

另外在代码中添加如下引用

use std::time::{Duration, Instant};
use crossbeam::select;
use crossbeam_channel::tick;
use crossbeam_channel::after;
use crossbeam_channel::unbounded;
use std::thread;

接下来,本文会基于不同的功能用例分别介绍在Rust和Golang中如何创建和使用Ticker,并进行对比。

Ticker
首先介绍Rust和Golang中如何创建和使用Ticker

Rust

在Rust的crat crossbeam_channel中使用 crossbeam_channel::tick 创建 Ticker

crossbeam_channel::tick 官方描述

/// Creates a receiver that delivers messages periodically.
///
/// The channel is bounded with capacity of 1 and never gets disconnected. Messages will be
/// sent into the channel in intervals of `duration`. Each message is the instant at which it is
/// sent.

翻译过来就是:
返回一个channel的receiver,这个channel会周期性的传递出来消息。
这个channel的容量是1,永远不会关闭。
每隔固定时间间隔发送消息到channel中,消息的值就是消息发送时刻的instant。

看下tick的源码如下,可以看到tick返回的是一个channel的Receiver

pub fn tick(duration: Duration) -> Receiver<Instant> {Receiver {flavor: ReceiverFlavor::Tick(Arc::new(flavors::tick::Channel::new(duration))),}
}

再进入到 flavors::tick::Channel::new 中看到 flavors::tick::Channel 的定义和方法如下

pub(crate) struct Channel {/// The instant at which the next message will be delivered.delivery_time: AtomicCell<Instant>,/// The time interval in which messages get delivered.duration: Duration,
}impl Channel {/// Creates a channel that delivers messages periodically.#[inline]pub(crate) fn new(dur: Duration) -> Self {Channel {delivery_time: AtomicCell::new(Instant::now() + dur),duration: dur,}}/// Attempts to receive a message without blocking.#[inline]pub(crate) fn try_recv(&self) -> Result<Instant, TryRecvError> {loop {let now = Instant::now();let delivery_time = self.delivery_time.load();if now < delivery_time {return Err(TryRecvError::Empty);}if self.delivery_time.compare_exchange(delivery_time, now + self.duration).is_ok(){return Ok(delivery_time);}}}/// Receives a message from the channel.#[inline]pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<Instant, RecvTimeoutError> {loop {let delivery_time = self.delivery_time.load();let now = Instant::now();if let Some(d) = deadline {if d < delivery_time {if now < d {thread::sleep(d - now);}return Err(RecvTimeoutError::Timeout);}}if self.delivery_time.compare_exchange(delivery_time, delivery_time.max(now) + self.duration).is_ok(){if now < delivery_time {thread::sleep(delivery_time - now);}return Ok(delivery_time);}}}/// Reads a message from the channel.#[inline]pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<Instant, ()> {token.tick.ok_or(())}/// Returns `true` if the channel is empty.#[inline]pub(crate) fn is_empty(&self) -> bool {Instant::now() < self.delivery_time.load()}/// Returns `true` if the channel is full.#[inline]pub(crate) fn is_full(&self) -> bool {!self.is_empty()}/// Returns the number of messages in the channel.#[inline]pub(crate) fn len(&self) -> usize {if self.is_empty() {0} else {1}}/// Returns the capacity of the channel.#[allow(clippy::unnecessary_wraps)] // This is intentional.#[inline]pub(crate) fn capacity(&self) -> Option<usize> {Some(1)}
}

注意上面 capacity中返回的是 Some(1) ,验证了容量是1的说法。这个容量是1的特性比较关键,在后面样例中会讲到。

快速上手

fn simple_ticker() {let start = Instant::now();let ticker = tick(Duration::from_millis(100));for _ in 0..5 {let msg = ticker.recv().unwrap();println!("{:?} elapsed: {:?}",msg, start.elapsed());}}

这个例子里面创建了一个间隔是100ms的ticker,每隔100ms就可以从ticker中获取一个message,输出如下

Instant { tv_sec: 355149, tv_nsec: 271585400 } elapsed: 100.0824ms
Instant { tv_sec: 355149, tv_nsec: 371585400 } elapsed: 200.3341ms
Instant { tv_sec: 355149, tv_nsec: 471585400 } elapsed: 300.3773ms
Instant { tv_sec: 355149, tv_nsec: 571585400 } elapsed: 400.2563ms
Instant { tv_sec: 355149, tv_nsec: 671585400 } elapsed: 500.379ms

ticker所在线程休眠

crossbeam_channel::tick 所关联的channel的容量是1,如果通道中已经有了message,那么后续新的message过来后就会被丢弃,我们可以用下面样例验证

fn sleep_ticker(){let ms = |ms| Duration::from_millis(ms);// Returns `true` if `a` and `b` are very close `Instant`s.// 如果时间 a 和 b 相差不到 50 milliseconds ,就返回truelet eq = |a,b| a+ms(50) > b &&  b+ms(50)>a;let start = Instant::now();// 定时器每隔 100 milliseconds 往r对应的channel中发送一个消息let r = tick(ms(100));// This message was sent 100 ms from the start and received 100 ms from the start.// tick开始100 ms 后,收到消息assert!(eq(r.recv().unwrap(), start + ms(100)));// 的确过去了 100 msassert!(eq(Instant::now(), start + ms(100)));// 这里 tick 对应的channel里面是空,所以在 [100ms 600ms] 线程休眠的时间内, 第200ms的消息仍然正常写入, 300ms,400ms,500ms,600ms的都无法写进去thread::sleep(ms(500));// This message was sent 200 ms from the start and received 600 ms from the start.// tick 开始后的200 ms ,收到消息// 这里到了第600ms时刻,channel里面有 200ms 的消息assert!(eq(r.recv().unwrap(), start + ms(200)));assert!(eq(Instant::now(), start + ms(600)));// 这里tick对应的channel又变为空了,所以700ms的消息可以正常写入// This message was sent 700 ms from the start and received 700 ms from the start.// tick 开始后的700ms,收到消息assert!(eq(r.recv().unwrap(), start + ms(700)));assert!(eq(Instant::now(), start + ms(700)));}

这个例子中,接受第一个100ms的消息后,当前thread睡眠了500ms,然后从ticker中接受了 Instant(200ms) 的消息,下一次接受的是 Instant(700ms) 的消息。 这个结果咋一看感觉特别诡异,为什么接受了 Instant(200ms) 的消息后,再次收到的就是 Instant(700ms) 的消息呢? 原因在于ticker绑定的channel的容量只有1,所以:

1,第100ms 消息到达时,channel是空,往channel中填入成功,然后 r.recv().unwap() 读取消息时候又把channel清空了
2,当前thread进入休眠
3,在当前thread休眠的期间,所以200ms消息到达时,channel是空,往channel中填入成功,此时channel满了。
4,随后300ms,400ms,500ms,600ms的消息到达时,channel是满的,消息被丢弃
5,600ms后当前thread醒来,执行 r.recv().unwap() 读取消息时候,此时channel中的是200ms的消息,所以读出来是Instant(200ms) ,并把channel清空了
6,700ms的消息到达时候,channel是空,往channel中填入成功, r.recv().unwap() 就读取到了 700ms 的消息。

参与 select

因为tick 返回的是 Receiver ,所以可以放入到 select 中跟正常 crossbeam_channel 创建的channel一同进行监听,如下

fn select_channl(){let start = Instant::now();let ticker = tick(Duration::from_millis(100));let (_s,r) = unbounded::<()>(); // 这里如果写成  let (_,r) = unbounded::<()>();  那么 r 就会一直可读,读取的数据 Err(RecvError)for _ in 0..5 {select! {recv(r)->msg=>{println!("recve {:?}",msg);},recv(ticker)->msg=>{println!("elapsed: {:?} {:?}", msg.unwrap(),start.elapsed());},}}
}
elapsed: Instant { tv_sec: 179577, tv_nsec: 291446700 } 100.4331ms
elapsed: Instant { tv_sec: 179577, tv_nsec: 391877000 } 200.8107ms
elapsed: Instant { tv_sec: 179577, tv_nsec: 492246700 } 301.2404ms
elapsed: Instant { tv_sec: 179577, tv_nsec: 592683200 } 401.4015ms
elapsed: Instant { tv_sec: 179577, tv_nsec: 692843600 } 501.5007ms

这里需要注意的是,上面let (_s,r) = unbounded::<()>(); 如果写成 let (_,r) = unbounded::<()>();的话, select! 就会一直进入到 recv®->msg分支中,读取的消息是 Err(RecvError) ​

具体原因跟 disconnection 有关, let (_,r) = unbounded::<()>();相当于Sender 在一开始就被drop掉了。

When all senders or all receivers associated with a channel get dropped, the channel becomes disconnected. No more messages can be sent, but any remaining messages can still be received. Send and receive operations on a disconnected channel never block.

翻译过来就是:

当一个channel所关联的所有sender 或 所有的receiver都被drop掉之后,这个channel就会变成 disconnected. 不可以再往里面发送消息,但是如果channel里面有剩余的消息可以继续接受。对一个 disconnected的channel进行Send或receive操作都不会阻塞。

golang

在golang里面使用 time.NewTicker 来创建一个Ticker 官方描述:

NewTicker returns a new Ticker containing a channel that will send the time on the channel after each tick. The period of the ticks is specified by the duration argument. The ticker will adjust the time interval or drop ticks to make up for slow receivers. The duration d must be greater than zero; if not, NewTicker will panic. Stop the ticker to release associated resources.

这里提到ticker可以使用Reset重置间隔,使用Stop来关闭ticker,不过Stop并不会关闭ticker所关联的channel。 ​

Ticker的内部源码实现

// A Ticker holds a channel that delivers ``ticks'' of a clock
// at intervals.
type Ticker struct {C <-chan Time // The channel on which the ticks are delivered.r runtimeTimer
}// NewTicker returns a new Ticker containing a channel that will send
// the time on the channel after each tick. The period of the ticks is
// specified by the duration argument. The ticker will adjust the time
// interval or drop ticks to make up for slow receivers.
// The duration d must be greater than zero; if not, NewTicker will
// panic. Stop the ticker to release associated resources.
func NewTicker(d Duration) *Ticker {if d <= 0 {panic(errors.New("non-positive interval for NewTicker"))}// Give the channel a 1-element time buffer.// If the client falls behind while reading, we drop ticks// on the floor until the client catches up.c := make(chan Time, 1)t := &Ticker{C: c,r: runtimeTimer{when:   when(d),period: int64(d),f:      sendTime,arg:    c,},}startTimer(&t.r)return t
}

可以看到golang的Ticker也是关联了一个channel,且这个channel的buffer长度也是1.

快速上手

func simple_ticker() {tick := time.NewTicker(time.Duration(100 * time.Millisecond))start := time.Now()for i := 0; i < 5; i++ {msg := <-tick.Cfmt.Printf("%v elapsed: %v\n", msg, time.Since(start))}
}

执行结果如下:

2021-09-26 11:50:11.3452615 +0800 CST m=+0.100763101 elapsed: 100.807ms
2021-09-26 11:50:11.4447965 +0800 CST m=+0.200297901 elapsed: 200.2688ms
2021-09-26 11:50:11.5453194 +0800 CST m=+0.300820801 elapsed: 300.7901ms
2021-09-26 11:50:11.6448699 +0800 CST m=+0.400371301 elapsed: 400.3422ms
2021-09-26 11:50:11.7452991 +0800 CST m=+0.500800501 elapsed: 500.7743ms

ticker所在线程休眠

golang版本的Ticker和 crossbeam_channel::tick 类似,也是关联了一个buffer长度是1的channel,所以执行的逻辑也和 crossbeam_channel::tick 的一致,这里就不多过解释。

func ms(d int) time.Duration {return time.Duration(d * int(time.Millisecond))
}
func eq(a, b time.Time) bool {return a.Add(ms(50)).After(b) && b.Add(ms(50)).After(a)
}func assert(a, b time.Time) {if !eq(a, b) {panic(a)}
}func sleep_ticker() {start := time.Now()// 定时器每隔 100 milliseconds 往r对应的channel中发送一个消息r := time.NewTicker(ms(100))defer r.Stop()// This message was sent 100 ms from the start and received 100 ms from the start.// tick开始100 ms 后,收到消息msg := <-r.Cassert(msg, start.Add(ms(100)))// 的确过去了 100 msassert(time.Now(), start.Add(ms(100)))// 这里 tick 对应的channel里面是空,所以在 [100ms 600ms] 线程休眠的时间内, 第200ms的消息仍然正常写入, 300ms,400ms,500ms,600ms的都无法写进去time.Sleep(ms(500))// This message was sent 200 ms from the start and received 600 ms from the start.// tick 开始后的200 ms ,收到消息// 这里到了第600ms时刻,channel里面有 200ms 的消息msg = <-r.Cassert(msg, start.Add(ms(200)))assert(time.Now(), start.Add(ms(600)))// 这里tick对应的channel又变为空了,所以700ms的消息可以正常写入// This message was sent 700 ms from the start and received 700 ms from the start.// tick 开始后的700ms,收到消息msg = <-r.Cassert(msg, start.Add(ms(700)))assert(time.Now(), start.Add(ms(700)))}

参与select

由于Ticker的成员C本身就是一个channel

type Ticker struct {C <-chan Time // The channel on which the ticks are delivered.r runtimeTimer
}

所以可以把Ticker.C 加入到select中,如下:

func select_ticker() {tick := time.NewTicker(time.Duration(100 * time.Millisecond))defer tick.Stop()start := time.Now()r := make(chan int)for i := 0; i < 5; i++ {select {case msg := <-tick.C:fmt.Printf("%v elapsed: %v\n", msg, time.Since(start))case <-r:fmt.Println("recv from r")}}
}

输出

2021-09-24 14:57:23.7813998 +0800 CST m=+0.100670501 elapsed: 100.6697ms
2021-09-24 14:57:23.8818368 +0800 CST m=+0.201107601 elapsed: 201.0681ms
2021-09-24 14:57:23.9814607 +0800 CST m=+0.300731501 elapsed: 300.7018ms
2021-09-24 14:57:24.0810694 +0800 CST m=+0.400340201 elapsed: 400.3102ms
2021-09-24 14:57:24.1815779 +0800 CST m=+0.500848601 elapsed: 500.8122ms

Timer

Rust

官网描述:https://docs.rs/crossbeam/0.8.1/crossbeam/channel/fn.after.html

Creates a receiver that delivers a message after a certain duration of time.
The channel is bounded with capacity of 1 and never gets disconnected. Exactly one message will be sent into the channel after duration elapses. The message is the instant at which it is sent.

翻译过来就是

创建一个channel的receiver,经过特定时间间隔后这个channel中会塞入消息
这个channel的容量是1,永远不会关闭。 每隔固定间隔发送消息到channel中,消息的值就是代表消息发送时刻的Instant。

看下after的源码如下,同样可以看到after返回的也是是一个channel的Receiver

pub fn after(duration: Duration) -> Receiver<Instant> {Receiver {flavor: ReceiverFlavor::At(Arc::new(flavors::at::Channel::new_timeout(duration))),}
}

进入到 flavors::at::Channel::new_timeout

/// Channel that delivers a message at a certain moment in time
pub(crate) struct Channel {/// The instant at which the message will be delivered.delivery_time: Instant,/// `true` if the message has been received.received: AtomicBool,
}impl Channel {/// Creates a channel that delivers a message at a certain instant in time.#[inline]pub(crate) fn new_deadline(when: Instant) -> Self {Channel {delivery_time: when,received: AtomicBool::new(false),}}/// Creates a channel that delivers a message after a certain duration of time.#[inline]pub(crate) fn new_timeout(dur: Duration) -> Self {Self::new_deadline(Instant::now() + dur)}/// Attempts to receive a message without blocking.#[inline]pub(crate) fn try_recv(&self) -> Result<Instant, TryRecvError> {// We use relaxed ordering because this is just an optional optimistic check.if self.received.load(Ordering::Relaxed) {// The message has already been received.return Err(TryRecvError::Empty);}if Instant::now() < self.delivery_time {// The message was not delivered yet.return Err(TryRecvError::Empty);}// Try receiving the message if it is still available.if !self.received.swap(true, Ordering::SeqCst) {// Success! Return delivery time as the message.Ok(self.delivery_time)} else {// The message was already received.Err(TryRecvError::Empty)}}/// Receives a message from the channel.#[inline]pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<Instant, RecvTimeoutError> {// We use relaxed ordering because this is just an optional optimistic check.if self.received.load(Ordering::Relaxed) {// The message has already been received.utils::sleep_until(deadline);return Err(RecvTimeoutError::Timeout);}// Wait until the message is received or the deadline is reached.loop {let now = Instant::now();let deadline = match deadline {// Check if we can receive the next message._ if now >= self.delivery_time => break,// Check if the timeout deadline has been reached.Some(d) if now >= d => return Err(RecvTimeoutError::Timeout),// Sleep until one of the above happensSome(d) if d < self.delivery_time => d,_ => self.delivery_time,};thread::sleep(deadline - now);}// Try receiving the message if it is still available.if !self.received.swap(true, Ordering::SeqCst) {// Success! Return the message, which is the instant at which it was delivered.Ok(self.delivery_time)} else {// The message was already received. Block forever.utils::sleep_until(None);unreachable!()}}/// Reads a message from the channel.#[inline]pub(crate) unsafe fn read(&self, token: &mut Token) -> Result<Instant, ()> {token.at.ok_or(())}/// Returns `true` if the channel is empty.#[inline]pub(crate) fn is_empty(&self) -> bool {// We use relaxed ordering because this is just an optional optimistic check.if self.received.load(Ordering::Relaxed) {return true;}// If the delivery time hasn't been reached yet, the channel is empty.if Instant::now() < self.delivery_time {return true;}// The delivery time has been reached. The channel is empty only if the message has already// been received.self.received.load(Ordering::SeqCst)}/// Returns `true` if the channel is full.#[inline]pub(crate) fn is_full(&self) -> bool {!self.is_empty()}/// Returns the number of messages in the channel.#[inline]pub(crate) fn len(&self) -> usize {if self.is_empty() {0} else {1}}/// Returns the capacity of the channel.#[allow(clippy::unnecessary_wraps)] // This is intentional.#[inline]pub(crate) fn capacity(&self) -> Option<usize> {Some(1)}
}

和tick一样,方法 capacity中返回的是 Some(1) ,表明 after 返回的Receiver所关联的chnnel的容量的确也是1。

快速上手

fn simple_after() {let start = Instant::now();let af = after(Duration::from_millis(100));for _ in 0..5 {af.recv().unwrap();println!("elapsed: {:?}", start.elapsed());}}

上面样例中,在100ms的时刻会往af关联channel写入消息,之后就不会再有新的消息到达af关联channel,所以af.recv() 在收到100ms 的消息后,后面就会永远阻塞,输出如下:

elapsed: 100.1125ms
^C

after所在线程休眠

fn sleep_after(){// Converts a number of milliseconds into a `Duration`.let ms = |ms| Duration::from_millis(ms);// Returns `true` if `a` and `b` are very close `Instant`s.let eq = |a, b| a + ms(50) > b && b + ms(50) > a;let start = Instant::now();let r = after(ms(100));thread::sleep(ms(500));// This message was sent 100 ms from the start and received 500 ms from the start.assert!(eq(r.recv().unwrap(), start + ms(100)));assert!(eq(Instant::now(), start + ms(500)));
}

由于当前thread休眠的时候,r关联的channel是空的,所以当100ms的消息到来的时候可以成功写入到r关联的channel中,然后等当前thread醒来后,执行 r.recv().unwrap() 就可以拿到100ms的消息。 ​

参与select

与 tick同理,after也可以参与到select中

fn select_after() {let start = Instant::now();let (_s, r) = unbounded::<i32>();let timeout = Duration::from_millis(100);select! {recv(r) -> msg => println!("received {:?}", msg),recv(after(timeout)) -> msg => println!("timed out {:?} {:?}",msg.unwrap(),start.elapsed()),}}

输出

timed out Instant { tv_sec: 181366, tv_nsec: 193851700 } 100.1291ms

at

https://docs.rs/crossbeam/0.8.1/crossbeam/channel/fn.at.html at和after比较类似,at是指定具体的时间点,官网描述如下:

Creates a receiver that delivers a message at a certain instant in time.
The channel is bounded with capacity of 1 and never gets disconnected. Exactly one message will be sent into the channel at the moment in time when. The message is the instant at which it is sent, which is the same as when. If when is in the past, the message will be delivered instantly to the receiver.

需要注意的是,如果指定的时间点比当前时间还要早,那么会立刻发送消息到channel ​

golang

使用 https://pkg.go.dev/time#Timer 官方描述

// NewTimer creates a new Timer that will send
// the current time on its channel after at least duration d.

结构体定义

type Timer struct {C <-chan Time// contains filtered or unexported fields
}// NewTimer creates a new Timer that will send
// the current time on its channel after at least duration d.
func NewTimer(d Duration) *Timer {c := make(chan Time, 1)t := &Timer{C: c,r: runtimeTimer{when: when(d),f:    sendTime,arg:  c,},}startTimer(&t.r)return t
}

也是关联一个缓存长度是1的channel。

快速上手

func simple_timer() {tm := time.NewTimer(time.Duration(100 * time.Millisecond))defer tm.Stop()start := time.Now()// 接受一次后就永远阻塞了,程序报死锁错误for i := 0; i < 2; i++ {msg := <-tm.Cfmt.Printf("%v elapsed: %v\n", msg, time.Since(start))}
}

上面样例中,在100ms的时刻会往 tm.C 写入消息,之后就不会再有新的消息到达 tm.C,所以 msg:=<-tm.C 在收到100ms 的消息后,后面就会永远阻塞,输出如下:

2021-09-24 15:08:56.8384098 +0800 CST m=+0.100381801 elapsed: 100.3695ms
fatal error: all goroutines are asleep - deadlock!

所在线程休眠

func sleep_timer() {start := time.Now()// 100 milliseconds 后 往r对应的channel中发送一个消息r := time.NewTimer(ms(100))defer r.Stop()//在休眠过程中的100ms的时候,往r.C中写入了100ms的time.Sleep(ms(500))// 此时里面的是 100ms 的消息msg := <-r.Cassert(msg, start.Add(ms(100)))// 的确过去了 500 msassert(time.Now(), start.Add(ms(500)))// 到这里就永远阻塞了msg = <-r.Cassert(msg, start.Add(ms(200)))assert(time.Now(), start.Add(ms(600)))}

当前thread休眠的时候,r.C 是空的,所以当100ms的消息到来的时候可以成功写入到 r.C 中,然后等当前thread醒来后,执行 msg := <-r.C 就可以拿到100ms的消息在进程输出。后面由于不会再有新的消息写入到r.C 中,执行 msg := <-r.C 就永远阻塞了。

fatal error: all goroutines are asleep - deadlock!

参与 select

与 time.Ticker 同理,可以把Timer.C 加入到select中. 下面代码中由于tm.C只会收到一次消息,所以for循环中在第二次时就永远阻塞了。

func select_timer() {tm := time.NewTimer(time.Duration(100 * time.Millisecond))defer tm.Stop()start := time.Now()r := make(chan int)for i := 0; i < 2; i++ {select {case msg := <-tm.C:fmt.Printf("%v elapsed: %v\n", msg, time.Since(start))case <-r:fmt.Println("recv from r")}}
}

输出

2021-09-24 15:13:09.9339061 +0800 CST m=+0.100970401 elapsed: 100.9088ms
fatal error: all goroutines are asleep - deadlock!

Instant

instant在Rust和golang中用来度量时间,它们的区别取下:

Rust

目前有两种方法可以获取当前时间 Instant::now 和 SystemTime::now.

1,Instant: 返回的是monotonic clock, 主要用于在计算时间间隔时候有用。
2,SystemTime: 返回的是 wall clock,主要用于跟文件系统或其它进程之间进行沟通使用。

在Rust中,这两个类型的打印结果对人类来说都是不直观的,也没有提供响应的格式化打印的方法。

golang

https://pkg.go.dev/time#Time

time.Now() 返回的Time类型包含 monotonic clock 。

1,如果 Time t 包含 monotonic clock,那么t.Add会同时把duration加到 wall clock和 monotonic clock 上。
2,由于 t.AddDate(y,m,d) ,t.Round(d) ,t.Truncate(d) 是 wall clock的计算,所以这些方法计算后的结果会去掉monotonic clock.
3,如果 Time t ,Time u 都包含 monotonic clock, 那么 t.After(u), t.Before(u), t.Equal(u), and t.Sub(u) 在计算的时候只会使用 monotonic clock. 否则如果u 或 t 任意一个不包含 monotonic clock, 就使用 wall clock来计算。
4,t.GobEncode, t.MarshalBinary, t.MarshalJSON, and t.MarshalText 会忽略掉 monotonic clock。
5,== 在计算的时候,不仅仅会比较 instant,还会比较 Location 和 monotonic clock.
6,程序在使用Time的时候应该传值而不是指针。也就是说 time 变量或结构体成员应该是类型 time.Time 而不是 *time.Time
7,Time 值类型的变量可以被多个goroutine安全的并发访问,除了 GobDecode, UnmarshalBinary, UnmarshalJSON and UnmarshalTex
8,Time instants 可以使用 Before, After,和 Equal 方法进行比较。
9,Each Time has associated with it a Location, consulted when computing the presentation form of the time, such as in the Format, Hour, and Year methods. The methods Local, UTC, and In return a Time with a specific location. Changing the location in this way changes only the presentation; it does not change the instant in time being denoted and therefore does not affect the computations described in earlier paragraphs.

Let‘s Go Rust 系列之定时器 Ticker Timer相关推荐

  1. 杰理AC695X系列---us定时器(12)

    杰理AC695X系列-us定时器(12) 前几日调了一个433的接收ic,要求的定时器是us级别的,但SDK里面自带的定时器是ms级别的,例如以下定时扫描增加接口 所以需要搞个us定时器,怎么搞? 办 ...

  2. wxpython应用实例_wxPython定时器wx.Timer简单应用实例

    # -*- coding: utf-8 -*- ######################################################## ## 这是wxPython定时器wx. ...

  3. WinForm LED循环显示信息,使用定时器Threading.Timer

    原文:WinForm LED循环显示信息,使用定时器Threading.Timer 这里用一个示例来演示timer如何使用. 示例:LED屏幕显示 描述:这个示例其实很简单,LED屏幕上显示3个信息: ...

  4. Arduino ESP32 通过定时器(Timer)功能唤醒深度睡眠

    Arduino ESP32 通过定时器(Timer)功能唤醒深度睡眠 通过定时器功能,设置5秒,时间到就唤醒深度睡眠 实例代码 /*通过定时器功能唤醒深度睡眠(设置为5秒) */#define uS_ ...

  5. python timer怎么用_python定时器(Timer)用法简单实例

    python定时器(Timer)用法简单实例 本文实例讲述了python定时器(Timer)用法.分享给大家供大家参考.具体如下: # encoding: UTF-8 import threading ...

  6. 通过定时器的Timer方式替代delay/Hal_delay函数进行定时

    文章目录 前言 一.设置一个5秒的定时器,每隔5秒从串口发送"hello windows!" 1.创建工程项目 2.项目配置 3.代码编写 1.重定向printf函数 2.定时器代 ...

  7. Java定时器(Timer)

    1.介绍 Timer和TimerTask是用于在后台线程中调度任务的java util类.简单地说,TimerTask是要执行的任务,Timer是调度器. 2.调度一次性任务 2.1 指定延迟后执行 ...

  8. 韦东山freeRTOS系列教程之【第十章】软件定时器(software timer)

    文章目录 系列教程总目录 概述 10.1 软件定时器的特性 10.2 软件定时器的上下文 10.2.1 守护任务 10.2.2 守护任务的调度 10.2.3 回调函数 10.3 软件定时器的函数 10 ...

  9. golang中定时器ticker

    package mainimport ("fmt""time" )func main() {t := time.Now().Unix() //秒时间戳fmt.P ...

最新文章

  1. REST接口设计规范
  2. 设备树下字符设备驱动
  3. 什么是补码,怎么求补码
  4. sh(Spring+Spring mvc+hibernate)——IEmpDao.java
  5. galaxy s8 android pc,手机秒变PC!三星Galaxy S8桌面模式曝光
  6. session一些基本的东西
  7. 【JVM】强引用、软引用、弱引用、虚引用分别是什么
  8. 5种回到顶部的写法从实现到增强
  9. 玩~成语接龙c++代码
  10. 如何将多个TXT合并成一个TXT,文件名称提取
  11. 快捷键,总结一些实用高效的快捷键
  12. Asp.Net 密码加密技术
  13. Codeforces - Serval and Rooted Tree
  14. 软件配置 | ios系统Clion下载、安装、配置环境
  15. 自媒体助手软件开发需具备哪些功能?
  16. 【Cocos2D-x 3.5实战】坦克大战(1)环境配置
  17. php+怎么加边框,ps给图片加上漂亮的边框-PS教程
  18. jQuery使用ajaxSubmit()提交表单以及AjaxSubmit的一些用法
  19. Java开源 Web开发框架 (一)
  20. Pytorch之nn.Conv1d学习个人见解

热门文章

  1. 用单片机的RAM虚拟U盘(文件系统:Fat16)
  2. 谷歌地图 图片保存_Google如何在地图上跟踪并保存您的一举一动
  3. html 英文逗号,英语写作中不可小觑的五大错误 逗号别乱用
  4. Chrome浏览器网页保存成图片
  5. Shiro之基本使用
  6. 运营公众号成功的三大要素
  7. 涉密台式计算机密码可以输入几次,涉密打印机、扫描仪等与涉密计算机之间不采用无线方式连接 - 作业在线问答...
  8. 西门子1200PLC与V90伺服驱动器 TO控制模式(即工艺对象的方式))
  9. 删除电脑上重复备份的图片
  10. 74cms|骑士cms|开源招聘系统,数据结构