1、說明
libuv 提供了一個執行緒池,可用於執行使用者程式碼,libuv 中的工作佇列中的任務會線上程池中執行
libuv 中的執行緒池在內部用於執行所有檔案系統操作以及 getaddrinfo() 和 getnameinfo() 請求
libuv 中的執行緒池的預設數量為4,可以在啟動時修改環境變數 UV_THREADPOOL_SIZE 來修改,最大值為 1024(1.30.0版本之前是128)
libuv 中的執行緒池是全域性的,並在所有事件迴圈之間共享,當特定的函式利用 uv_queue_work() 方法使用工作佇列時,libuv 會預分配執行緒池,以較小的記憶體開銷(128個執行緒為1MB),來提高執行緒效能
以下三種型別的操作會在全域性執行緒池中進行:
- 檔案系統操作;
- DNS函式(getaddrinfo 和 getnameinfo);
- 使用 uv_queue_work() 排程的使用者程式碼;
需要注意的是,即使使用了執行緒池,libuv 的方法也不是執行緒安全的
2、API
2.1、uv_queue_work
int uv_queue_work(uv_loop_t* loop,
uv_work_t* req,
uv_work_cb work_cb,
uv_after_work_cb after_work_cb);
新增一個任務到工作佇列中,在主執行緒中呼叫
loop: 事件迴圈
req: 傳入到任務的資料,一般使用 req.data 引數傳遞
work_cb: 執行方法
after_work_cb: 執行方法完成後執行
work_cb 方法會在函式中執行,after_work_cb 方法在建立執行緒中執行
void (*uv_work_cb)(uv_work_t* req);
void (*uv_after_work_cb)(uv_work_t* req, int status);
如果呼叫 uv_cancel 方法取消了佇列,則 uv_after_work_cb 的 status 為 UV_ECANCELED
2.2、uv_cancel
int uv_cancel(uv_req_t* req);
取消未執行的佇列中的任務,在任務中呼叫
req 為任務的引數
如果呼叫此方法取消了任務,則 after_work_cb 回撥函式的 status 的值為 UV_ECANCELED;
3、程式碼示例
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <uv.h>
void print(uv_work_t *req)
{
sleep(1);
long num = (long)req->data;
printf("thread id is: %ld, num is: %d\n", uv_thread_self(), num);
}
void after_print(uv_work_t *req, int status)
{
printf("after print, req data is %d, status is %d\n", req->data, status);
}
int main()
{
uv_loop_t *loop = uv_default_loop();
uv_work_t req[5];
for (int index = 0; index < 5; index++)
{
req[index].data = (void *)(long)index;
uv_queue_work(loop, &req[index], print, after_print);
sleep(1);
}
return uv_run(loop, UV_RUN_DEFAULT);
}
示例中的程式碼,每次執行 print() 方法都是在不同執行緒中,after_print() 方法和 main() 方法在同一個執行緒中