任务队列TaskQueue
是WebRTC中非常核心的一部分,其主要功能是将任务投递到某一个线程执行。TaskQueue
是WebRTC中进程交互很重要的方式。
本文主要分析TaskQueue
中最重要的基类TaskQueueBase
。
TaskQueue
机制中涉及的其他类后续会继续补充。
WebRTC版本:M84
任务队列不可避免地涉及到多线程的知识,此处仅简单介绍一下TaskQueueBase
部分涉及到的相关内容以及函数。
线程局部存储(TLS,Thread Local Storage)是线程私有的全局变量。普通的全局变量是多个线程共享的,一个线程对其修改,所有线程均可见。而线程局部存储是线程私有的,每个线程都有自己的一个副本,某个线程对其所做修改只会修改自己的副本,不会影响到其他线程的副本。
pthread_key_t
ABSL_CONST_INIT pthread_key_t g_queue_ptr_tls = 0;
上述声明中pthread_key_t
为前一小节提到的线程局部存储类型
Tls
相关函数说明int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_setspecific(pthread_key_t key, const void *value);
void* pthread_getspecific(pthread_key_t key);
pthread_key_create
函数可以创建pthread_key_t
变量。该函数需要提供两个参数,第一个参数是需要创建的pthread_key_t
变量,第二个参数是一个释放函数,在线程释放其Tls
的时候被调用。如果函数指针被设置成nullptr
,那么系统将调用默认释放函数。该函数成功创建变量时返回0,其他任何返回值均代表出现异常。pthread_setspcific
函数。该函数需要提供两个参数,第一个参数为前面声明的pthread_key_t
变量,第二个为void*
变量,也就意味着可以存储任何类型的值。pthread_getspecific
函数。该函数的参数为前面提到的pthread_key_t
变量,该函数返回void *
类型的值,如需使用,则要进行强制类型转换。InitializeTls
void InitializeTls(){
RTC_CHECK(pthread_key_create(&g_queue_ptr_tls, nullptr)==0);
}
RTC_CHECK()
为WebRTC中的断言宏,当其内部参数为false
时,抛出异常,直接中止当前进程。
GetQueuePtrTls
pthread_key_t GetQueuePtrTls(){
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
RTC_CHECK(pthread_once(&init_once, &InitializeTls) == 0);
return g_queue_ptr_tls;
}
调用pthread_once()
函数,传入初值为PTHREAD_ONCE_INIT
的init_once
变量,结合RTC_CHECK()
断言保证InitializeTls()
函数仅执行一次。
WEBRTC_POSIX
宏定义的前提下被定义,否则不被定义TaskQueueBase
类使用QueuedTask
类Path:
api\task_queue\queued_task.h
namespace webrtc{
class QueuedTask{
public:
virtual ~QueuedTask() = default;
virtual bool Run() = 0;
};// class QueuedTask
} // namespace webrtc
QueueTask
是一个抽象类,为需要异步执行的任务提供基本接口。
这个接口由一个单一的函数Run()
组成,它会在目标队列上执行,Run()
具体实现的功能由其子类决定。
TaskQueueBase
类Path:
api\task_queue\task_queue_base.h
api\task_queue\task_queue_base.cc
namespace webrtc{
class RTC_LOCKABLE RTC_EXPORT TaskQueueBase {
public:
virtual void Delete() = 0;
virtual void PostTask(std::unique_ptr<QueuedTask> task) = 0;
virtual void PostDelayedTask(std::unique_ptr<QueuedTask> task,
uint32_t milliseconds) = 0;
static TaskQueueBase* Current();
bool IsCurrent() const { return Current == this; }
protected:
class CurrentTaskQueueSetter{
public:
explicit CurrentTaskQueueSetter(TaskQueueBase* task_queue);
CurrentTaskQueueSetter(const CurrentTaskQueueSetter&) = delete;
CurrentTaskQueueSetter& operator=(const CurrentTaskQueueSetter&) = delete;
~CurrentTaskQueueSetter();
private:
TaskQueueBase* const previous_;
};// class CurrentTaskQueueSetter
virtual ~TaskQueueBase() = default;
};// class TaskQueueBase
} // namespace webrtc
Delete
virtual void Delete() = 0;
纯虚函数,基类中无需实现。
调用此函数开始销毁任务队列,此函数在返回时需要确保没有任务正在运行,也没有新的任务能够在任务队列中启动。
同时此函数负责释放对象,释放动作可以在Delete
期间同步进行,也可以在Delete
之后异步进行。
销毁某个任务队列对不在此任务队列中的任务不产生任何影响,这些任务也不会因为其他的任务队列销毁而调用任何函数。
在任务队列上执行的任务不可以调用Delete
,但是可以调用其他的函数,比如PostTask
。
PostTask
virtual void PostTask(std::unique_ptr<QueuedTask> task) = 0;
纯虚函数,基类中无需实现。
此函数用于安排一个即时任务的处理,这些任务按照先进先出的顺序执行(所以称为任务队列)。
如果task->Run()
的返回值是true
,代表任务成功执行,任务会在下一个QueuedTask
开始执行之前从任务队列中被移除。
PostDelayTask
virtual void PostDelayedTask(std::unique_ptr<QueuedTask> task,
uint32_t milliseconds) = 0;
纯虚函数,基类中无需实现。
此函数用于安排一个延迟任务的处理,处理会在调用PostDelayedTask
函数后的milliseconds
毫秒后执行。
关于延迟时间的精度可以称作“尽力而为”,在某些场景下,定时可能会有一些毫秒级的误差。
Current
和IsCurrent
需要说明的是,从此处开始,相关的的定义会根据所定义的宏而不同,代码块中会给出相关说明。
变量及函数声明:
static TaskQueueBase* Current();
bool IsCurrent() const { return Current() == this; }
//if defined ABSL_HAVE_THREAD_LOCAL
ABSL_CONST_INIT thread_local TaskQueueBase* current = nullptr;
Current()
函数定义:
// defined ABSL_HAVE_THREAD_LOCAL
TaskQueueBase* TaskQueueBase::Current() {
return current;
}
// defined WEBRTC_POSIX
TaskQueueBase* TaskQueueBase::Current() {
return static_cast<TaskQueueBase*>(pthread_getspecific(GetQueuePtrTls()));
}
Current()
函数返回当前线程保存的任务队列,返回值为一个static
变量。
IsCurrent()
函数则用于判断当前线程保存的任务队列是不是对象本身。
TaskQueueBase
是抽象基类,只用于提供接口,不可以被实例化TaskQueueBase
的析构函数被声明为虚函数TaskQueueBase
的成员类会在下一节介绍TaskQueue::CurrentTaskQueueSetter
Path:
api\task_queue\task_queue_base.h
api\task_queue\task_queue_base.cc
namespace webrtc {
class RTC_LOCKABLE RTC_EXPORT TaskQueueBase {
//...
protected:
class RTC_EXPORT CurrentTaskQueueSetter {
public:
explicit CurrentTaskQueueSetter(TaskQueueBase* task_queue);
CurrentTaskQueueSetter(const CurrentTaskQueueSetter&) = delete;
CurrentTaskQueueSetter& operator=(const CurrentTaskQueueSetter&) = delete;
~CurrentTaskQueueSetter();
private:
TaskQueueBase* const previous_;
};// class CurrentTaskQueueSetter
//...
};// class TaskQueueBase
}// namespace webrtc
CurrentTaskQueueSetter
// defined ABSL_HAVE_THREAD_LOCAL
TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter(
TaskQueueBase* task_queue)
: previous_(current) {
current = task_queue;
}
// defined WEBRTC_POSIX
TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter(
TaskQueueBase* task_queue)
: previous_(TaskQueueBase::Current()) {
pthread_setspecific(GetQueuePtrTls(), task_queue);
}
构造函数主要的任务分为两部分:
previous_
暂存当前线程的任务队列task_queue
存放在当前线程的Tls
中~CurrentTaskQueueSetter
// defined ABSL_HAVE_THREAD_LOCAL
TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter() {
current = previous_;
}
// defined WEBRTC_POSIX
TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter() {
pthread_setspecific(GetQueuePtrTls(), previous_);
}
构造函数的任务就是将之前暂存在previous_
的任务队列取出,重新放回到当前线程的Tls
当中。
CurrentTaskQueueSetter
类只在构造和析构时执行任务:
TaskQueueBase
类作为任务队列机制的核心基类,在后续分析中经常会涉及到,了解其实现方法有助于了解整个任务队列的运行机制。
关于任务队列的其他类,后续会在其他文章中进行分析。