先创建一个工程用于运行示例。如何创建工程,参考《WebRTC源码分析之工程-project》,在src\examples\BUILD.gn中添加如下内容:
rtc_executable("webrtc_learn"){
testonly = true
sources = [
"webrtclearn/main.cc"
]
deps = [
"../rtc_base:rtc_base",
"../rtc_base:rtc_task_queue_stdlib",
"../api/task_queue:task_queue"
]
}
#include
#include
#include "rtc_base/task_queue.h"
#include "rtc_base/task_queue_stdlib.h"
#include "rtc_base/platform_thread.h"
#include "api/task_queue/queued_task.h"
#include "api/task_queue/task_queue_base.h"
#include "api/task_queue/task_queue_factory.h"
using namespace std;
using namespace webrtc;
using namespace rtc;
/*定义即时任务*/
class instant_task :public QueuedTask
{
public:
instant_task(string thread_name)
:thread_name_(thread_name)
{}
bool Run() override
{
cout << thread_name_ << " post a task..." << endl;
return true;
}
private:
string thread_name_;
};
/*定义延迟任务*/
class delay_task : public QueuedTask
{
public:
delay_task(string thread_name)
: thread_name_(thread_name)
{}
bool Run() override
{
cout << thread_name_ << " post a task..." << endl;
return true;
}
private:
string thread_name_;
};
void other_thread(void* arg)
{
TaskQueue* tq = (TaskQueue*)arg;
unique_ptr<QueuedTask> instantTask(new instant_task("child_thread"));
unique_ptr<QueuedTask> delayedTask(new delay_task("child_thread"));
/*延迟任务3秒后执行*/
tq->PostDelayedTask(std::move(delayedTask), 3 * 1000);
/*这个任务是最先执行*/
tq->PostTask(std::move(instantTask));
/*睡眠5秒等待任务被执行*/
Sleep(5000);
}
int main()
{
/*定义一个工厂*/
unique_ptr<TaskQueueFactory> tqFactory = CreateTaskQueueStdlibFactory();
/*用工厂创建任务队列*/
unique_ptr<TaskQueueBase, TaskQueueDeleter> tqb = tqFactory->CreateTaskQueue("task queue", TaskQueueFactory::Priority::NORMAL);
/*托管任务队列*/
TaskQueue tq(std::move(tqb));
/*开启一个子线程*/
PlatformThread other_th(other_thread, (void*)&tq, "child_thread");
other_th.Start();
/*睡眠1秒后投递任务*/
Sleep(1000);
/*投递即时任务*/
unique_ptr<QueuedTask> task(new instant_task("main_thread"));
tq.PostTask(std::move(task));
/*回收线程*/
other_th.Stop();
return 0;
}
在创建任务队列时,会创建一个线程,这个线程只用于任务的处理,其他线程可以向其投递任务。在主线程中又创建了一个子线程,并将任务队列传入,在子线程也可以向其投递任务。
TaskQueueBase类所在文件的位置:src\api\task_queue\task_queue_base.h task_queue_base.cc
class RTC_LOCKABLE 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; }
/*需要将析构器置为虚函数,为了完整的析构。*/
virtual ~TaskQueueBase() = default;
}
TaskQueueBase类是虚基类,不能实例化,只能用于提供接口。在释放任务队列的时候,不能直接将其删除,需要等待任务队列处理完正在处理的任务后,才能释放任务队列。
class CurrentTaskQueueSetter
{
public:
explicit CurrentTaskQueueSetter(TaskQueueBase* task_queue);
CurrentTaskQueueSetter(const CurrentTaskQueueSetter&) = delete;
CurrentTaskQueueSetter& operator=(const CurrentTaskQueueSetter&) = delete;
~CurrentTaskQueueSetter();
private:
TaskQueueBase* const previous_; /*保存的是上一个任务队列*/
};
用于把任务队列绑定到当前线程上
/*创建线程局部存储*/
ABSL_CONST_INIT pthread_key_t g_queue_ptr_tls = 0;
/*全局的函数*/
void InitializeTls()
{
/*创建TLS*/
RTC_CHECK(pthread_key_create(&g_queue_ptr_tls, nullptr) == 0);
}
/*全局的函数*/
pthread_key_t GetQueuePtrTls()
{
/*保证InitializeTls()只运行一次*/
static pthread_once_t init_once = PTHREAD_ONCE_INIT;
RTC_CHECK(pthread_once(&init_once, &InitializeTls) == 0);
return g_queue_ptr_tls;
}
线程局部存储(TLS,Thread Local Storage)是线程私有的全局变量。普通的全局变量是多个线程共享的,一个线程对其修改,所有线程均可见。而线程局部存储是线程私有的,每个线程都有自己的一个副本,某个线程对其所做修改只会修改自己的副本,不会影响到其他线程的副本。
线程局部存储的创建通过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)函数。
TaskQueueBase::CurrentTaskQueueSetter::CurrentTaskQueueSetter(TaskQueueBase* task_queue)
: previous_(TaskQueueBase::Current()) /*将当前任务队列保存下来*/
{
/*任务队列保存在当前线程的TLS中*/
pthread_setspecific(GetQueuePtrTls(), task_queue);
}
在生成CurrentTaskQueueSetter对象时,会把指定的任务队列保存在当前线程内。
TaskQueueBase::CurrentTaskQueueSetter::~CurrentTaskQueueSetter()
{
/*将之前的任务队列保存到线程内*/
pthread_setspecific(GetQueuePtrTls(), previous_);
}
CurrentTaskQueueSetter对象被释放时,需要将之前的任务队列重新保存到线程内。
TaskQueueBase* TaskQueueBase::Current()
{
return static_cast<TaskQueueBase*>(pthread_getspecific(GetQueuePtrTls()));
}
返回当前线程保存的任务队列
TaskQueueStdlib类所在文件的位置:src\rtc_base\task_queue_stdlib.cc
/*用于将主线程阻塞或唤醒*/
rtc::Event started_;
rtc::Event stopped_;
/*用于将任务处理线程阻塞或唤醒*/
rtc::Event flag_notify_;
/*任务处理线程的操作句柄*/
rtc::PlatformThread thread_;
/*标识任务处理线程是否退出*/
bool thread_should_quit_ RTC_GUARDED_BY(pending_lock_){false};
/*任务序号种子*/
using OrderId = uint64_t;
OrderId thread_posting_order_ RTC_GUARDED_BY(pending_lock_){};
/*存放即时任务*/
std::queue<std::pair<OrderId, std::unique_ptr<QueuedTask>>> pending_queue_ RTC_GUARDED_BY(pending_lock_);
/*存放延迟任务*/
std::map<DelayedEntryTimeout, std::unique_ptr<QueuedTask>> delayed_queue_ RTC_GUARDED_BY(pending_lock_);
RTC_GUARDED_BY(pending_lock_)是宏函数,使用clang
编译器时被展开为__attribute__((guarded_by(pending_lock_)))。这是给编译器使用的,让编译器检查在使用前面的变量时需要获取pending_lock_锁。
即时任务存放在队列中,按照FIFO执行任务。延迟任务存放在map中,按照延迟时间排序。
先作一个约定,创建任务队列对象的线程叫作主线程
,由主线程
创建的用于处理任务的线程叫作任务处理线程
。
TaskQueueStdlib::TaskQueueStdlib(absl::string_view queue_name,rtc::ThreadPriority priority)
: started_(false,false), /*初始化事件*/
stopped_(false,false),
flag_notify_(false,false), /*创建任务处理线程对象*/
thread_(&TaskQueueStdlib::ThreadMain, this, queue_name, priority)
{
/*创建任务处理线程,并开始执行入口函数。*/
thread_.Start();
/*主线程被挂起*/
started_.Wait(rtc::Event::kForever);
}
在任务处理线程未就绪之前,主线程会被挂起,任务处理线程就绪以后会再次把主线程唤醒。
void TaskQueueStdlib::ThreadMain(void* context)
{
/*将参数强转成TaskQueueStdlib* */
TaskQueueStdlib* me = static_cast<TaskQueueStdlib*>(context);
/*将任务队列注册到当前线程中*/
CurrentTaskQueueSetter set_current(me);
/*开始处理任务*/
me->ProcessTasks();
}
这是任务处理线程真正的入口函数,通过传参的方式获取任务队列对象,将任务队列保存到任务处理线程中,准备工作做好后,开始处理其他线程投递的任务。
void TaskQueueStdlib::Delete()
{
/*销毁操作不能在任务处理线程中进行*/
RTC_DCHECK(!IsCurrent());
{
rtc::CritScope lock(&pending_lock_);
/*标记任务处理线程需要退出*/
thread_should_quit_ = true;
}
/*唤醒线程去执行任务*/
NotifyWake();
/*主线程被阻塞,等待任务处理线程退出时唤醒主线程。*/
stopped_.Wait(rtc::Event::kForever);
/*回收任务处理线程*/
thread_.Stop();
/*释放任务队列对象*/
delete this;
}
要销毁任务队列对象时,需要主动调用Delete()函数,等待任务处理线程处理完正在处理的任务,任务处理线程退出后,需要回收线程资源。
void TaskQueueStdlib::NotifyWake()
{
/*唤醒任务处理线程*/
flag_notify_.Set();
}
任务处理线程将所有任务处理完毕后,没有事做了,会进入睡眠状态。当有新的任务到达以后,需要唤醒线程去处理任务。
/*投递即时任务*/
void TaskQueueStdlib::PostTask(std::unique_ptr<QueuedTask> task)
{
{
/*上锁*/
rtc::CritScope lock(&pending_lock_);
/*任务序号递增*/
OrderId order = thread_posting_order_++;
/*将任务推到队列中保存*/
pending_queue_.push(std::pair<OrderId, std::unique_ptr<QueuedTask>>(order, std::move(task)));
}
/*唤醒任务处理线程去处理任务*/
NotifyWake();
}
thread_posting_order_和pending_queue_存在着多线程访问,需要上锁。
投递了即时任务以后需要激活任务处理线程去处理任务。
/*投递延迟任务*/
void TaskQueueStdlib::PostDelayedTask(std::unique_ptr<QueuedTask> task,uint32_t milliseconds)
{
/*任务执行的时间,绝对时间。*/
auto fire_at = rtc::TimeMillis() + milliseconds;
DelayedEntryTimeout delay;
delay.next_fire_at_ms_ = fire_at; /*设置延迟任务的执行时间*/
{
rtc::CritScope lock(&pending_lock_);
/*获取延迟任务的序号*/
delay.order_ = ++thread_posting_order_;
/*将延迟任务添加到延迟任务队列*/
delayed_queue_[delay] = std::move(task);
}
/*唤醒线程去执行任务*/
NotifyWake();
}
投递延迟任务,任务会在指定的时间被执行。
struct DelayedEntryTimeout
{
int64_t next_fire_at_ms_{};
OrderId order_{};
/*用于比较大小*/
bool operator<(const DelayedEntryTimeout& o) const
{
/*对两个参数依次比较*/
return std::tie(next_fire_at_ms_, order_) <
std::tie(o.next_fire_at_ms_, o.order_);
}
};
在保存延迟任务的时候,需要记录延迟任务的执行时间和任务序号。延迟任务的执行顺序,首先按照时间顺序,时间早的先执行,若执行时间相同,则按照序号,序号小的先执行,也就是先到的先执行。
struct NextTask
{
bool final_task_{false}; /*是否释放任务处理队列对象*/
std::unique_ptr<QueuedTask> run_task_; /*实际的任务*/
int64_t sleep_time_ms_{}; /*任务处理线程休眠时间*/
};
对任务及其必要信息进行封装
/*获取下一个任务*/
TaskQueueStdlib::NextTask TaskQueueStdlib::GetNextTask()
{
/*初始化为nullptr*/
NextTask result{};
/*此刻的时间,用于检测延迟任务是否可以执行。*/
auto tick = rtc::TimeMillis();
rtc::CritScope lock(&pending_lock_);
/*任务处理线程需要退出*/
if (thread_should_quit_)
{
result.final_task_ = true;
/*直接返回,退出任务处理线程。*/
return result;
}
/*延迟任务列表中有任务*/
if (delayed_queue_.size() > 0)
{
/*获取延迟最小的任务*/
auto delayed_entry = delayed_queue_.begin();
/*获取任务的信息*/
const auto& delay_info = delayed_entry->first;
/*获取任务对象 queuedTask*/
auto& delay_run = delayed_entry->second;
/*若到达了延迟任务的执行时间*/
if (tick >= delay_info.next_fire_at_ms_)
{
/*即时任务队列中也有任务待执行*/
if (pending_queue_.size() > 0)
{
auto& entry = pending_queue_.front();
auto& entry_order = entry.first;
auto& entry_run = entry.second;
/*需要选择序号小的任务执行*/
if (entry_order < delay_info.order_)
{
/*即时任务的序号小*/
result.run_task_ = std::move(entry_run);
/*从队列中弹出*/
pending_queue_.pop();
/*执行即时任务*/
return result;
}
}
/*延迟任务的序号小*/
result.run_task_ = std::move(delay_run);
/*从map中删除*/
delayed_queue_.erase(delayed_entry);
/*执行延迟任务*/
return result;
}
/*注意:执行到这里时result.run_task是nullptr*/
/*延迟任务没有到执行时间,计算下次延迟任务的执行时间。*/
result.sleep_time_ms_ = delay_info.next_fire_at_ms_ - tick;
}
/*如果没有延迟任务可执行,则在即时任务中取出一个任务。*/
if (pending_queue_.size() > 0)
{
auto& entry = pending_queue_.front();
result.run_task_ = std::move(entry.second);
pending_queue_.pop();
}
/*
* result.run_task为nullptr,result.sleep_time_ms_不为0时:
* 没有即时任务也没有要执行的延迟任务
*
* result.run_task不为nullptr,result.sleep_time_ms_为0时:
* 有即时任务但没有延迟任务
*
* result.run_task不为nullptr,result.sleep_time_ms_不为0时:
* 有即时任务但没有可执行的延迟任务
*
* result.run_task为nullptr,result.sleep_time_ms_为0时:
* 任何任务都没有
*/
return result;
}
在即时任务和延迟任务都可以执行时,需要按照任务的序号执行任务,保证任务总体上按照FIFO执行。
void TaskQueueStdlib::ProcessTasks()
{
/*任务处理线程就绪,激活主线程。*/
started_.Set();
while (true)
{
/*从任务表(队列、map)中获取了一个待执行的任务。*/
auto task = GetNextTask();
/*首先判断,是否要结束本任务队列线程*/
if (task.final_task_)
break; /*要销毁任务处理线程了,跳出循环。*/
/*判断是否有任务需要处理*/
if (task.run_task_)
{
QueuedTask* release_ptr = task.run_task_.release();
/*执行任务*/
if (release_ptr->Run())
delete release_ptr;
/*获取下一个任务,继续执行。*/
continue;
}
if (0 == task.sleep_time_ms_)
/*说明没有延迟任务也没有即时任务,则一直睡眠,直到被激活。*/
flag_notify_.Wait(rtc::Event::kForever);
else
/*有待执行的延迟任务,则线程睡眠指定的时间。*/
flag_notify_.Wait(task.sleep_time_ms_);
}
/*通知主线程,本线程准备退出了。*/
stopped_.Set();
}
task.run_task_不为nullptr,说明有任务待执行,可能是即时任务也可能是延迟任务。当task.sleep_time_ms_为0表示没有任何任务需要执行,若不为0表示有延迟任务待执行,并且延迟任务在task.sleep_time_ms_毫秒后执行,没有任务时,任务处理线程进入睡眠。
QueuedTask类所在文件的位置:src\api\task_queue\queued_task.h
class QueuedTask
{
public:
virtual ~QueuedTask() = default;
virtual bool Run() = 0;
};
所有需要执行的任务都需要继承此类,并覆写Run()函数。
TaskQueue类所在文件的位置:src\rtc_base\task_queue.h task_queue.cc
使用代理模式
对任务队列进行了简单的包装
class RTC_LOCKABLE RTC_EXPORT TaskQueue
{
public:
using Priority = ::webrtc::TaskQueueFactory::Priority;
explicit TaskQueue(std::unique_ptr<webrtc::TaskQueueBase,webrtc::TaskQueueDeleter> task_queue);
~TaskQueue();
bool IsCurrent() const;
webrtc::TaskQueueBase* Get() { return impl_; }
void PostTask(std::unique_ptr<webrtc::QueuedTask> task);
void PostDelayedTask(std::unique_ptr<webrtc::QueuedTask> task,uint32_t milliseconds);
private:
webrtc::TaskQueueBase* const impl_; /*保存着托管的任务队列对象*/
RTC_DISALLOW_COPY_AND_ASSIGN(TaskQueue);
};
TaskQueue类主要是对任务队列对象进行托管,任务队列对象在释放时需要主动调用Delete()函数。
TaskQueue::TaskQueue(std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter> task_queue)
: impl_(task_queue.release()) {}
TaskQueue::~TaskQueue()
{
/*主动调用Delete()函数释放对象*/
impl_->Delete();
}
bool TaskQueue::IsCurrent() const
{
return impl_->IsCurrent();
}
void TaskQueue::PostTask(std::unique_ptr<webrtc::QueuedTask> task)
{
return impl_->PostTask(std::move(task));
}
void TaskQueue::PostDelayedTask(std::unique_ptr<webrtc::QueuedTask> task,uint32_t milliseconds)
{
return impl_->PostDelayedTask(std::move(task), milliseconds);
}
这些函数都是进行了一层简单的包装
TaskQueueDeleter类所在文件的位置:src\api\task_queue\task_queue_base.h
struct TaskQueueDeleter
{
/*仿函数,用于释放TaskQueueBase。*/
void operator()(TaskQueueBase* task_queue) const
{
task_queue->Delete();
}
};
在unique_ptr指针中用于释放TaskQueueBase对象,TaskQueueBase对象的释放需要主动调用Delete()安全的释放。
在unique_ptr指针在释放托管的对象时,默认使用delete释放对象,也可以使用用户指定的释放器。示例如下:
#include
#include
using namespace std;
class A
{
public:
A(){ cout <<this<< ": constructor" << endl;}
~A(){ cout << this << ": deconstructor" << endl;}
};
class Deleter
{
public:
void operator()(A* p)
{
cout << p << ": delete A ..." << endl;
delete p; /*调用析构器*/
}
};
int main()
{
/*使用用户提供的释放器*/
unique_ptr<A, Deleter> pa(new A());
return 0;
}
TaskQueueFactory类所在文件的位置:src\api\task_queue\task_queue_factory.h
class TaskQueueFactory
{
public:
enum class Priority { NORMAL = 0, HIGH, LOW };
virtual ~TaskQueueFactory() = default;
virtual std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(absl::string_view name,Priority priority) const = 0;
};
创建任务队列的工厂类
TaskQueueStdlibFactory类所在文件的位置:src\rtc_base\task_queue_stdlib.cc
rtc::ThreadPriority TaskQueuePriorityToThreadPriority(TaskQueueFactory::Priority priority)
{
switch (priority)
{
case TaskQueueFactory::Priority::HIGH:
return rtc::kRealtimePriority;
case TaskQueueFactory::Priority::LOW:
return rtc::kLowPriority;
case TaskQueueFactory::Priority::NORMAL:
return rtc::kNormalPriority;
default:
RTC_NOTREACHED();
return rtc::kNormalPriority;
}
}
任务队列的优先级转换成线程的优先级
class TaskQueueStdlibFactory final : public TaskQueueFactory
{
public:
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(absl::string_view name,Priority priority) const override
{
return std::unique_ptr<TaskQueueBase, TaskQueueDeleter>(new TaskQueueStdlib(name, TaskQueuePriorityToThreadPriority(priority)));
}
};
调用CreateTaskQueue()函数创建TaskQueueStdlib对象时,需要指定释放器,不能单纯的调用delete释放TaskQueueStdlib对象。
std::unique_ptr<TaskQueueFactory> CreateTaskQueueStdlibFactory()
{
return absl::make_unique<TaskQueueStdlibFactory>();
}
CreateTaskQueueStdlibFactory()是全局函数用于创建TaskQueueStdlibFactory工厂对象,TaskQueueStdlibFactory工厂对象可以用于创建TaskQueueStdlib对象。
本文仅介绍了TaskQueueBase类的一个子类TaskQueueStdlib,TaskQueueLibevent和TaskQueueWin等也继承自TaskQueueBase类,暂时还不清楚这些类之间的本质区别,以后会逐个分析这些类,但掌握TaskQueueStdlib的思想对于分析其他类会十分有帮助。