closure
- Chromium多线程通信用到的Closure实际上是一个特殊的Callback。
1 2 3 |
void MyFunc(int i, const std::string& str) {} base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23); cb.Run("hello world"); |
- 如果把上面创建的Callback对象看作是一个Closure,那么它首先会被发送到目标线程的消息队列中,然后再在目标线程中调用它的成员函数Run,最后就会导致函数MyFunc在目标线程中被调用。
- Callback对象还可以与一个类的成员函数绑定,如下:
这里绑定的类对象就为myclass,并且是以裸指针的形式指定的。这时候我们就需要保证Callback对象cb的成员函数Run被调用时,对象myclass还没有被销毁。
1 2 3 4 5 6 7 |
class MyClass { public: void MyFunc(int i, const std::string& str) {} }; MyClass* myclass = new MyClass; base::Callback<void(void)> cb = base::Bind(&MyClass::MyFunc, myclass, 23, "hello world"); |
- 为了更方便地管理被绑定对象的生命周期,函数base::Bind允许通过scoped_refptr智能指针来绑定类对象,如下:
Callback对象cb使用完成之后,对象myclass会自动释放,这样就可以保证Callback对象cb的成员函数Run被调用时,对象myclass是存在的。
也可以通过函数GetWeakPtr获得对象myclass的一个WeakPtr弱智能指针,这样当Callback对象cb的成员函数Run被调用时,如果对象myclass已经被销毁,那么就不会调用它的成员函数MyFunc。
1 2 |
scoped_refptr<MyClass> myclass(new MyClass); base::Callback<void(void)> cb = base::Bind(&MyClass::MyFunc, myclass, 23, "hello world"); |
callback
- Callback类是从CallbackBase类继承下来的。CallbackBase类有两个成员变量bind_state_和polymorphicinvoke。
- bind_state_指向的是一个BindState对象,该BindState对象是保存了其宿主Callback对象创建时绑定的函数以及参数。
- polymorphic_invoke_指向的是一个InvokeFuncStorage对象,该InvokeFuncStorage对象描述的是Invoker类的静态成员函数Run。
- BindState类是从BindStateBase类继承下来的,BindStateBase类又是从RefCountedThreadSafe类继承下来的。可以配合智能指针来使用。
- BindState类将宿主Callback对象创建时绑定的参数保存在成员变量p1_、p2_等中,绑定的函数则保存在成员变量runnable_指向的一个RunnableAdapter对象的成员变量function_中。
- 当CallbackBase类的成员函数Run被调用时,它们通过成员变量polymorphic_invoke_调用Invoker类的静态成员函数Run。
- Invoker类的静态成员函数Run从BindState对象分别取出与它绑定的参数p1_、p2_等,以及RunnableAdapter对象,连同传递给CallbackBase类的成员函数Run的参数,一起再传递给InvokeHelper类的静态成员函数MakeItSo。
- InvokeHelper类的静态成员函数MakeItSo接下来又会调用传递给它的RunnableAdapter对象的成员函数Run,后者又会调用保存在其成员变量function_的函数,这时候调用的实际上就是其关联的Callback对象绑定的函数。
多线程模型
概述
- 线程经过短暂的启动之后(Start),就围绕着一个任务队列(TaskQueue)不断地进行循环,直到被通知停止为止(Stop)。
- 在围绕任务队列循环期间,它会不断地检查任务队列是否为空。如果不为空,那么就会将里面的任务(Task)取出来,并且进行处理。
这样,一个线程如果要请求另外一个线程执行某一个操作,那么只需要将该操作封装成一个任务,并且发送到目标线程的任务队列去即可。 - Thread是一个用来创建带消息循环的类。当我们创建一个Thread对象后,调用它的成员函数Start或者StartWithOptions就可以启动一个带消息循环的线程。
thread
- Thread类继承了PlatformThread::Delegate类,并且重写了它的成员函数ThreadMain(跨平台)。
- Thread类有一个重要的成员变量messageloop\,它指向的是一个MessageLoop对象。
- 这个MessageLoop对象就是用来描述线程的消息循环的。
- MessageLoop类内部通过成员变量run_loop_指向的一个RunLoop对象和成员变量pump_指向的一个MessagePump对象来描述一个线程的消息循环。
- 一个线程在运行的过程中,可以有若干个消息循环,也就是一个消息循环可以运行在另外一个消息循环里面。除了最外层的消息循环,其余的消息的消息循环称为嵌套消息循环。
- RunLoop类有三个重要的成员变量:
- messageloop\,记录一个RunLoop对象关联的MessageLoop对象。
- previousloop\,记录前一个消息循环,当就是包含当前消息循环的消息循环。
- rundepth\,记录消息循环的嵌套深度。
- MessageLoop类的成员变量pump_指向的一个MessagePump对象是用来进行消息循环的,也就是说,Thread类描述的线程通过MessagePump类进入到消息循环中去。
- Thread类将消息划分为三类
- workqueue,指向一个TaskQueue对象,用来保存那些需要马上处理的消息。
- delayed_workqueue,指向一个DelayedTaskQueue,用来保存那些需要延迟一段时间再处理的消息。
- deferred_non_nestable_workqueue,指向一个TaskQueue对象,用来保存那些不能够在嵌套消息循环中处理的消息。
- 一个MessagePump对象在进行消息循环时,如果发现消息队列中有消息,那么就需要通知关联的MessageLoop对象进行处理。通知使用的接口就通过MessagePump::Delegate类来描述。
- DoWork,用来通知MessageLoop类处理其成员变量work_queue_保存的消息。
- DoDelayedWork,用来通知MessageLoop类处理其成员变量delayed_work_queue_保存的消息。
- DoIdleWork,用来通知MessageLoop类当前无消息需要处理,MessageLoop类可以利用该间隙做一些Idle Work。
- GetQueueingInformation,用来获取MessageLoop类内部维护的消息队列的信息,例如消息队列的大小,以及下一个延迟消息的处理时间。
- Thread类的成员函数StartWithOptions首先是将线程启动参数封装一个在栈上分配的StartupData对象中,并且这个StartupData对象的地址会保存在Thread类的成员变量startup_data_中。接下来再调用由平台实现的PlatformThread类的静态成员函数Create创建一个线程。
最后通过StartupData对象的成员变量event描述的一个WaitableEvent对象等待上述创建的线程启动完成。
当新创建的线程启动完成之后,就会通过上述的WaitableEvent对象唤醒当前线程,当前线程将Thread类的成员变量startup_data_置为NULL,避免它引用一个即将无效的在栈上分配的StartupData对象,并且将Thread类的成员变量started_的值设置为true,表示新创建的线程已经启动完毕。
消息循环
- MessageLoop类的成员函数Init中,创建了一个任务队列,并且保存在成员变量incoming_queue_中。
任务队列是一个IncomingQueue类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
class BASE_EXPORT IncomingTaskQueue : public RefCountedThreadSafe<IncomingTaskQueue> { public: ...... bool AddToIncomingQueue(const tracked_objects::Location& from_here, const Closure& task, TimeDelta delay, bool nestable); ...... void ReloadWorkQueue(TaskQueue* work_queue); ...... void WillDestroyCurrentMessageLoop(); ...... private: ...... TaskQueue incoming_queue_; ...... MessageLoop* message_loop_; ...... }; |
- IncomingQueue类有两个重要的成员变量:
- incomingqueue,它描述的是一个TaskQueue,代表的是线程的消息队列,也就是所有发送给线程的消息都保存在这里。
- messageloop,它指向一个MessageLoop对象,描述的是线程的消息循环。
- 消息贲类型:
- 类型为MessageLoop::TYPE_UI的消息循环对应的消息泵为MessagePumpForUI。
在Chromium中,消息循环类型为MessageLoop::TYPE_UI的线程称为UI线程,也就是应用程序的主线程。 - 类型为MessageLoop::TYPE_IO,那么对应的消息泵为MessagePumpForIO。
在Chromium中,消息循环类型为MessageLoop::TYPE_IO的线程称为IO线程,但是这里的IO不是读写文件的意思,而是执行IPC的意思。 - 其他类型
- 类型为MessageLoop::TYPE_UI的消息循环对应的消息泵为MessagePumpForUI。
- 消息循环的层次关系
- 一个MessageLoop对象的成员函数RunHandler进入消息循环前后,RunLoop类的成员函数Run分别调用了BeforeRun和AfterRun两个成员函数,目的就是为了建立好消息循环的层次关系。
WaitableEvent
- WaitableEvent是有效地实现线程消息循环的一个重要类。通过WaitableEvent类,线程可以在无消息处理时进入睡眠状态,并且在有消息处理时从睡眠状态唤醒过来,从而避免了不断地轮循消息队列是否有消息处理的操作。因为消息队列可能在大多数情况下都是空的,对它进行不断轮循将会浪费CPU周期。
向一个线程的消息队列发送消息
- MessageLoop类的成员函数PostTask、PostDelayedTask、 PostNonNestableTask和PostNonNestableDelayedTask实现的。
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!