🏤问题来由
在使用libuv的过程中,我们难免遇见的一个问题是,有一些库没有异步、只能同步运行,这种情况该怎么办呢?比如mysql-connector-cpp。
首先要说的是,直接在回调函数中执行mysql-connector-cpp这种会阻塞的操作是不符合Libuv的reactor模式的。
1 | void handle_json_lab(std::shared_ptr<smpHttp::HttpRequest> req,std::shared_ptr<smpHttp::HttpResponse> res) |
上面这样便是错误的案例。我在写这个项目时,之前就采用了这样的错误做法。
我的这个项目是个http后台,我在接受到POST请求,直接在回调函数中执行mysql操作,这时整个主线程就阻塞住了1,而这就意味着我的http后台不再能接受任何请求,只能等待mysql操作完成后,回调函数返回。而这个mysql的操作耗时一般在3s以上,这对我这个Http后台来说是毁灭性的打击。。。。
🌆解决办法
在手册Thread pool work scheduling中为我们这样的需求提供了这样一个函数:
uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb)。
这个函数就是上面我们问题的解决办法。但是要注意的是uv_async_t不可以替代这个。虽然都是执行用户的函数。async是让用户函数直接被主线程在uv_run中运行,而uv_queue_work是将work_cb提交给子线程执行,完成后通知主线程,主线程在uv_run中执行after_work_cb。
总结下来就是:uv_async_t用来执行不阻塞的任务,uv_queue_work执行要阻塞的任务(考虑到线程切换的消耗一般不用来执行不阻塞的任务)
🉐看看源码
这一部分可以结合这我的这篇文章-libuv源码分析(5)uv_fs_*来看。可以作为佐证,libuv中对着这类没有自带异步版本的阻塞操作的处理是一样的:让子线程去执行这个任务,避免阻塞主线程的事件循环,完成后子线程通知主线程。
uv_queue_work源码:
1 | int uv_queue_work(uv_loop_t* loop, |
再结合我的这篇文章-libuv源码分析(5)uv_fs_*中uv_fs_*函数的源码,这些操作可以总结成以下代码:
1 | UV_REQ_INIT(req, typ); //初始化基类uv_req_t |