Mac和Windows UI线程比较

mac上在11.0之前,有些函数必须要放在主线程(主UI线程)执行才可以,放在自己起的UI线程就不行,那说明,一个进程支持多个UI线程,很可能也是最近几年刚支持的,之前的系统无论win还是mac,可能确实是只有主线程才是UI线程。

为什么要自己起一个UI线程? 1. 某些函数,必须要在UI线程执行, 2. 某些系统的消息,必须要放在UI线程中接收

mac和win其实对于自己启动的UI线程,比较如下:

winmac
需要用std::thread创建一个新线程,该线程为UI线程
在该新线程里面,要自己写消息循环是,写法: while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg);// ExcuteMyTask(); }是,写法1:CFRunLoopRun(); // 该函数会阻塞住,猜测该函数内部有一个while循环写法2: while (1) { // NSRunLoop manages autorelease pools itself. [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; }
如何执行自定义的task?在上面的while循环中,增加一个每次执行task的步骤1. 用这种方法加入runloop work_source_ = CFRunLoopSourceCreate(NULL, // allocator 1, // priority &source_context); CFRunLoopAddSource(run_loop(), work_source_, kCFRunLoopCommonModes);用 CFRunLoopSourceSignal(work_source_); CFRunLoopWakeUp(run_loop_); 唤醒
如何接收系统的消息?把回调函数放在lpfnWndProc里面,然后再注册一下窗口mac上如果要接收系统消息,注册的时候都要传递个runloop给注册的函数,可以把刚才新启动的UI线程中,用CFRunLoopGetCurrent()拿到runloop,然后把这个传出来

windows消息队列和Mac的RunLoop对比

两者存在的意义类似,都是为了接收系统消息,比如windows上,接收鼠标,键盘的消息,首先系统有一个全局的消息队列,这个是存在在内核态的,然后如果某个进程想接收鼠标消息,则必须要有一个UI线程(看进程是如何创建的,如果是单纯的console程序,那主线程不是UI线程,需要自己创建一个UI线程,如果是用创建向导创建的对话框或其他带窗体的程序,那主线程就是UI线程,不需要手动再写 while (::GetMessage(&msg, nullptr, 0, 0)) {

::TranslateMessage(&msg);

::DispatchMessage(&msg);

},系统已经默认把这段代码实现了,window和mac对系统消息的定义不太一样,比如win上认为video设备插拔事件,必须要在UI线程里面接收,但mac就不需要必须在UI线程接收,而hid的事件,mac就必须要在RunLoop里面接收。mac也类似,控制台程序没有runloop,只有GUI程序才会默认主线程是UI线程,并且自动创建主线程RunLoop,可以用CFRunLoopGetMain获得主线程的RunLoop,也可以手动创建RunLoop,是用

CFRunLoopGetCurrent,第一次调用的时候会创建一个和该线程对应的RunLoop,从bt all的结果看,大概率是apple自己又创建了一个线程,这个线程专跑runloop循环,在mac里面,一个线程对应一个runloop。在windows里面,必须要手动写while(GetMessage()..)这一段代码,在windows里面,调用CreateWindow,RegisterClassA的线程是用户自己创建的UI线程,还需要手动写这一段while(GetMessage)代码,才能接收消息。

在mac下,RunLoop的应用场景和windows的消息循环不同,runloop更像是系统提供的一个可以接受系统消息+定时器+线程间通信的一套机制,并不强调和UI强相关,其中CFRunLoopSourceCreate CFRunLoopAddSource 就可以认为是创建了一个定时器,如果类比thread和timer,一个线程可以有多个timer,比如fun1需要3s执行一次,fun2需要5s执行一次,那这是两个timer,可以放在一个线程里面。所以一个runloop可以有多个source,用这种AddSource就是添加“timer", CFRunLoopSourceContext shutdownEvent;

std::memset(&shutdownEvent, 0, sizeof(shutdownEvent));

shutdownEvent.info = runLoop;

shutdownEvent.perform = (void (*)(void*))CFRunLoopStop;

用这种方式,把timer的回调fun1 = perform,然后用CFRunLoopSourceSignal + CFRunLoopWakeUp 去主动执行一次fun1函数,不同于timer的是,runloop的这种source都是一次执行的。也就是source1基本就是系统事件,source0基本就是应用层事件。主线程的RunLoop不需要手动起,如果是自己写的runloop,需要调用CFRunLoopRun()手动把runloop起起来。

再说回windows的消息机制,一个UI线程可以对应多个窗口,每次dispatchMessage,其实就是遍历该线程对应的所有窗口的lpfnWndProc函数。网上的说法是不正确的,一个进程可以有多个UI线程的。

如果线程之间想发送消息,也可以用这种机制,发送端调用SendMessage或PostMessage,然后就走到了接收线程的while(GetMessage)这个循环l里面了,注意,也可以跨进程调用SendMessage实现进程间通信。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值