tokio task的通信和同步(1): 简介

通常来说,对于允许并发多执行分支的内核或引擎来说,都需要提供对应的通信机制和同步机制。

例如,多进程之间,有进程间通信方式,比如管道、套接字、共享内存、消息队列等,还有进程间的同步机制,例如信号量、文件锁、条件变量等。多线程之间,也有线程间通信方式,简单粗暴的是直接共享同进程内存,同步机制则有互斥锁、条件变量等。

tokio提供了异步多任务的并发能力,它也需要提供异步任务之间的通信方式和同步机制。

在介绍它们之前,需要先开启tokio的同步功能。

tokio = {version = "1.13", features = ["rt", "sync", "rt-multi-thread"]}

sync模块功能简介

sync模块主要包含两部分功能:异步任务之间的通信模块以及异步任务之间的状态同步模块。

任务间通信

tokio的异步任务之间主要采用消息传递(message passing)的通信方式,即某个异步任务负责发消息,另一个异步任务收消息。这种通信方式的最大优点是避免并发任务之间的数据共享,消灭数据竞争,使得代码更加安全,更加容易维护。

消息传递通常使用通道(channel)来进行通信。tokio提供几种不同功能的通道:

  • oneshot通道: 一对一发送的一次性通道,即该通道只能由一个发送者(Sender)发送最多一个数据,且只有一个接收者(Receiver)接收数据
  • mpsc通道: 多对一发送,即该通道可以同时有多个发送者向该通道发数据,但只有一个接收者接收数据
  • broadcast通道: 多对多发送,即该通道可以同时有多个发送者向该通道发送数据,也可以有多个接收者接收数据
  • watch通道: 一对多发送,即该通道只能有一个发送者向该通道发送数据,但可以有多个接收者接收数据

不同类型的通道,用于解决不同场景的需求。通常来说,最常用的是mpsc类型的通道。

任务间状态同步

在编写异步任务的并发代码时,很多时候需要去检测任务之间的状态。比如任务A需要等待异步任务B执行完某个操作后才允许向下执行。

比较原始的解决方式是直接用代码去轮询判断状态是否达成。但在异步编程过程中,这类状态检测的需求非常普遍,因此异步框架会提供一些内置在框架中的同步原语。同步原语封装了各种状态判断、状态等待的轮询操作,这使得编写任务状态同步的代码变得更加简单直接。

通常来说,有以下几种基本的同步原语,这些也是tokio所提供的:

  • Mutex: 互斥锁,任务要执行某些操作时,必须先申请锁,只有申请到锁之后才能执行操作,否则就等待
  • RwLock: 读写锁,类似于互斥锁,但粒度更细,区分读操作和写操作,可以同时存在多个读操作,但写操作必须独占锁资源
  • Notify: 任务通知,用于唤醒正在等待的任务,使其进入就绪态等待调度
  • Barrier: 屏障,多个任务在某个屏障处互相等待,只有这些任务都达到了那个屏障点,这些任务才都继续向下执行
  • Semaphore: 信号量(信号灯),限制同时执行的任务数量,例如限制最多只有20个线程(或tokio的异步任务)同时执行