成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

通過N-API使用Libuv線程池

系統 Windows
Node.js不適合處理耗時操作是一直存在的問題,為此Node.js提供了三種解決方案。

 [[403810]]

本文轉載自微信公眾號「編程雜技」,作者theanarkh。轉載本文請聯系編程雜技公眾號。

Node.js不適合處理耗時操作是一直存在的問題,為此Node.js提供了三種解決方案。

1 子進程

2 子線程

3 Libuv線程池

前兩種是開發效率比較高的,因為我們只需要寫js。但是也有些缺點

1 執行js的成本

2 雖然可以間接使用Libuv線程池,但是受限于Node.js提供的API。

3 無法利用c/c++層提供的解決方案(內置或業界的)。

這時候我們可以嘗試第三種解決方案。直接通過N-API使用Libuv線程池。下面我們看看這么做。N-API提供了幾個API。

  1. napi_create_async_work // 創建一個worr,但是還沒有執行 
  2. napi_delete_async_work // 釋放上面創建的work的內存 
  3. napi_queue_async_work // 往Libuv提交一個work 
  4. napi_cancel_async_work // 取消Libuv中的任務,如果已經在執行則無法取消 

接下來我們看看如何通過N-API使用Libuv線程池。首先看看js層。

  1. const { submitWork } = require('./build/Release/test.node'); 
  2. submitWork((sum) => { 
  3.     console.log(sum
  4. }) 

js提交一個任務,然后傳入一個回調。接著看看N-API的代碼。

  1. napi_value Init(napi_env env, napi_value exports) { 
  2.   napi_value func; 
  3.   napi_create_function(env, 
  4.                       NULL
  5.                       NAPI_AUTO_LENGTH, 
  6.                       submitWork, 
  7.                       NULL
  8.                       &func); 
  9.   napi_set_named_property(env, exports, "submitWork", func); 
  10.   return exports; 
  11.  
  12. NAPI_MODULE(NODE_GYP_MODULE_NAME, Init) 

首先定義導出的函數,接著看核心邏輯。

1 定義一個結構體保存上下文

  1. struct info 
  2.   int sum; // 保存計算結果 
  3.   napi_ref func; // 保存回調 
  4.   napi_async_work worker; // 保存work對象 
  5. }; 

2 提交任務到Libuv

  1. static napi_value submitWork(napi_env env, napi_callback_info info) { 
  2.   napi_value resource_name; 
  3.   napi_status status; 
  4.    
  5.   size_t argc = 1; 
  6.   napi_value args[1]; 
  7.   struct info data = {0, nullptr, nullptr}; 
  8.   struct info * ptr = &data; 
  9.   status = napi_get_cb_info(env, info, &argc, args, NULLNULL); 
  10.   if (status != napi_ok) { 
  11.     goto done; 
  12.   } 
  13.   napi_create_reference(env, args[0], 1, &ptr->func); 
  14.   status = napi_create_string_utf8(env,"test", NAPI_AUTO_LENGTH, &resource_name); 
  15.   if (status != napi_ok) { 
  16.     goto done; 
  17.   } 
  18.   // 創建一個work,ptr保存的上下文會在work函數和done函數里使用 
  19.   status = napi_create_async_work(env, nullptr, resource_name, work, done, (void *) ptr, &ptr->worker); 
  20.   if (status != napi_ok) { 
  21.     goto done; 
  22.   } 
  23.   // 提及work到Libuv 
  24.   status = napi_queue_async_work(env, ptr->worker); 
  25.  
  26.   done:  
  27.     napi_value ret; 
  28.     napi_create_int32(env, status == napi_ok ? 0 : -1, &ret); 
  29.     return  ret; 

執行上面的函數,任務就會被提交到Libuv線程池了。

3 Libuv子線程執行任務

  1. void work(napi_env env, void* data) { 
  2.   struct info *arg = (struct info *)data; 
  3.   printf("doing...\n"); 
  4.   int sum = 0; 
  5.   for (int i = 0; i < 10; i++) { 
  6.     sum += i; 
  7.   } 
  8.   arg->sum = sum

很簡單,計算幾個數。并且保存結果。

4 回調js

  1. void done(napi_env env, napi_status status, void* data) { 
  2.   struct info *arg = (struct info *)data; 
  3.   if (status == napi_cancelled) { 
  4.     printf("cancel..."); 
  5.   } else if (status == napi_ok) { 
  6.     printf("done...\n"); 
  7.     napi_value callback; 
  8.     napi_value global;   
  9.     napi_value result; 
  10.     napi_value sum
  11.     // 拿到結果 
  12.     napi_create_int32(env, arg->sum, &sum); 
  13.     napi_get_reference_value(env, arg->func, &callback); 
  14.     napi_get_global(env, &global); 
  15.     // 回調js 
  16.     napi_call_function(env, global, callback, 1, &sum, &result); 
  17.     // 清理 
  18.     napi_delete_reference(env, arg->func); 
  19.     napi_delete_async_work(env, arg->worker); 
  20.   } 

并且執行后,我們看到輸出了45。接下來我們分析大致的過程。首先我呢看看ThreadPoolWork,ThreadPoolWork是對Libuv work的封裝。

  1. class ThreadPoolWork { 
  2.  public
  3.   explicit inline ThreadPoolWork(Environment* env) : env_(env) { 
  4.     CHECK_NOT_NULL(env); 
  5.   } 
  6.   inline virtual ~ThreadPoolWork() = default
  7.  
  8.   inline void ScheduleWork(); 
  9.   inline int CancelWork(); 
  10.  
  11.   virtual void DoThreadPoolWork() = 0; 
  12.   virtual void AfterThreadPoolWork(int status) = 0; 
  13.  
  14.   Environment* env() const { return env_; } 
  15.  
  16.  private: 
  17.   Environment* env_; 
  18.   uv_work_t work_req_; 
  19. }; 

類的定義很簡單,主要是封裝了uv_work_t。我們看看每個函數的意義。DoThreadPoolWork和AfterThreadPoolWork是虛函數,由子類實現,我們一會看子類的時候再分析。我們看看ScheduleWork

  1. void ThreadPoolWork::ScheduleWork() { 
  2.   env_->IncreaseWaitingRequestCounter(); 
  3.   int status = uv_queue_work( 
  4.       env_->event_loop(), 
  5.       &work_req_, 
  6.       // Libuv子線程里執行的任務函數 
  7.       [](uv_work_t* req) { 
  8.         ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req); 
  9.         self->DoThreadPoolWork(); 
  10.       }, 
  11.       // 任務處理完后的回調 
  12.       [](uv_work_t* req, int status) { 
  13.         ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req); 
  14.         self->env_->DecreaseWaitingRequestCounter(); 
  15.         self->AfterThreadPoolWork(status); 
  16.       }); 
  17.   CHECK_EQ(status, 0); 

ScheduleWork是負責給Libuv提交任務的函數。接著看看CancelWork。

  1. int ThreadPoolWork::CancelWork() { 
  2.   return uv_cancel(reinterpret_cast<uv_req_t*>(&work_req_)); 

直接調用Libuv的函數取消任務。看完父類,我們看看子類的定義,子類在N-API里實現。

  1. class Work : public node::AsyncResource, public node::ThreadPoolWork { 
  2.  private: 
  3.   explicit Work(node_napi_env env, 
  4.                 v8::Local<v8::Object> async_resource, 
  5.                 v8::Local<v8::String> async_resource_name, 
  6.                 napi_async_execute_callback execute
  7.                 napi_async_complete_callback complete = nullptr, 
  8.                 void* data = nullptr) 
  9.     : AsyncResource(env->isolate, 
  10.                     async_resource, 
  11.                     *v8::String::Utf8Value(env->isolate, async_resource_name)), 
  12.       ThreadPoolWork(env->node_env()), 
  13.       _env(env), 
  14.       _data(data), 
  15.       _execute(execute), 
  16.       _complete(complete) { 
  17.   } 
  18.  
  19.   ~Work() override = default
  20.  
  21.  public
  22.   static Work* New(node_napi_env env, 
  23.                    v8::Local<v8::Object> async_resource, 
  24.                    v8::Local<v8::String> async_resource_name, 
  25.                    napi_async_execute_callback execute
  26.                    napi_async_complete_callback complete, 
  27.                    void* data) { 
  28.     return new Work(env, async_resource, async_resource_name, 
  29.                     execute, complete, data); 
  30.   } 
  31.   // 釋放該類對象的內存 
  32.   static void Delete(Workwork) { 
  33.     delete work
  34.   } 
  35.   // 執行用戶設置的函數 
  36.   void DoThreadPoolWork() override { 
  37.     _execute(_env, _data); 
  38.   } 
  39.  
  40.   void AfterThreadPoolWork(int status) override { 
  41.    // 執行用戶設置的回調 
  42.     _complete(env, ConvertUVErrorCode(status), _data); 
  43.   } 
  44.  
  45.  private: 
  46.   node_napi_env _env; 
  47.   // 用戶設置的數據,用于保存執行結果等 
  48.   void* _data; 
  49.   // 執行任務的函數 
  50.   napi_async_execute_callback _execute; 
  51.   // 任務處理完的回調 
  52.   napi_async_complete_callback _complete; 
  53. }; 

在Work類我們看到了虛函數DoThreadPoolWork和AfterThreadPoolWork的實現,沒有太多邏輯。最后我們看看N-API提供的API的實現。

  1. napi_status napi_create_async_work(napi_env env, 
  2.                                    napi_value async_resource, 
  3.                                    napi_value async_resource_name, 
  4.                                    napi_async_execute_callback execute
  5.                                    napi_async_complete_callback complete, 
  6.                                    void* data, 
  7.                                    napi_async_work* result) { 
  8.   v8::Local<v8::Context> context = env->context(); 
  9.  
  10.   v8::Local<v8::Object> resource; 
  11.   if (async_resource != nullptr) { 
  12.     CHECK_TO_OBJECT(env, context, resource, async_resource); 
  13.   } else { 
  14.     resource = v8::Object::New(env->isolate); 
  15.   } 
  16.  
  17.   v8::Local<v8::String> resource_name; 
  18.   CHECK_TO_STRING(env, context, resource_name, async_resource_name); 
  19.  
  20.   uvimpl::Workwork = uvimpl::Work::New(reinterpret_cast<node_napi_env>(env), 
  21.                                          resource, 
  22.                                          resource_name, 
  23.                                          execute
  24.                                          complete, 
  25.                                          data); 
  26.  
  27.   *result = reinterpret_cast<napi_async_work>(work); 
  28.  
  29.   return napi_clear_last_error(env); 

napi_create_async_work本質上是對Work的簡單封裝,創建一個Work并返回給用戶。

2 napi_delete_async_work

  1. napi_status napi_delete_async_work(napi_env env, napi_async_work work) { 
  2.   CHECK_ENV(env); 
  3.   CHECK_ARG(env, work); 
  4.  
  5.   uvimpl::Work::Delete(reinterpret_cast<uvimpl::Work*>(work)); 
  6.  
  7.   return napi_clear_last_error(env); 

napi_delete_async_work用于任務執行完后釋放Work對應的內存。

3 napi_queue_async_work

  1. napi_status napi_queue_async_work(napi_env env, napi_async_work work) { 
  2.   CHECK_ENV(env); 
  3.   CHECK_ARG(env, work); 
  4.  
  5.   napi_status status; 
  6.   uv_loop_t* event_loop = nullptr; 
  7.   status = napi_get_uv_event_loop(env, &event_loop); 
  8.   if (status != napi_ok) 
  9.     return napi_set_last_error(env, status); 
  10.  
  11.   uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work); 
  12.  
  13.   w->ScheduleWork(); 
  14.  
  15.   return napi_clear_last_error(env); 

napi_queue_async_work是對ScheduleWork的封裝,作用是給Libuv線程池提交任務。

4 napi_cancel_async_work

  1. napi_status napi_cancel_async_work(napi_env env, napi_async_work work) { 
  2.   CHECK_ENV(env); 
  3.   CHECK_ARG(env, work); 
  4.  
  5.   uvimpl::Work* w = reinterpret_cast<uvimpl::Work*>(work); 
  6.  
  7.   CALL_UV(env, w->CancelWork()); 
  8.  
  9.   return napi_clear_last_error(env); 

napi_cancel_async_work是對CancelWork的封裝,即取消Libuv線程池的任務。我們看到一層層套,沒有太多邏輯,主要是要符合N-API的規范。

總結:通過N-API提供的API,使得我們不再受限于Nod.js本身提供的一些異步接口(使用Libuv線程池的接口),而是直接使用Libuv線程池,這樣我們不僅可以自己寫c/c++,還可以復用業界的一些解決方案解決Node.js里的一些耗時任務。

倉庫:https://github.com/theanarkh/learn-to-write-nodejs-addons

 

責任編輯:武曉燕 來源: 編程雜技
相關推薦

2021-06-06 08:30:29

N-APIPromiseAPI

2020-09-07 07:33:01

NodejsCPU密集型

2012-02-29 13:26:20

Java

2023-08-02 08:03:08

Python線程池

2021-09-11 15:26:23

Java多線程線程池

2021-02-06 14:02:55

線程池Builder模式

2021-07-11 23:25:29

Libuvepoll文件

2021-02-01 08:28:24

Linux線程池Linux系統

2023-06-08 07:48:03

Java線程池

2020-04-29 14:10:44

Java線程池編程語言

2023-05-19 08:01:24

Key消費場景

2024-05-21 11:09:17

2023-10-12 08:29:06

線程池Java

2024-07-15 08:20:24

2023-06-07 13:49:00

多線程編程C#

2015-03-24 16:29:55

默認線程池java

2024-11-27 08:15:50

2022-03-05 23:29:18

LibuvwatchdogNode.js

2021-06-06 23:40:53

線程池使用場景

2014-12-24 10:00:07

Spring
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 夜夜操天天操 | 国产精品区二区三区日本 | 日日日操 | 自拍偷拍一区二区三区 | 中文一区二区视频 | 97久久久久久久久 | 日本国产欧美 | 久久精品无码一区二区三区 | 五月天婷婷狠狠 | 欧美国产日韩成人 | 久久久久网站 | 性欧美xxxx | 欧美精品一区二区三区蜜桃视频 | 日韩成人一区 | 精品国产一区一区二区三亚瑟 | 免费高潮视频95在线观看网站 | 国产成人短视频在线观看 | 成人性视频免费网站 | 日韩av在线播 | 欧美在线观看一区二区 | 欧美日韩综合精品 | 人操人免费视频 | 人人九九精 | 国产一区二区三区在线 | 欧美精品在线一区 | 宅男噜噜噜66一区二区 | 欧美一区二区黄 | 亚洲精品成人在线 | 日韩精品一区二区三区中文在线 | 亚洲欧美综合精品久久成人 | 国产精品久久久久久久久免费相片 | 999免费观看视频 | 日韩精品一区二区三区免费视频 | 国产不卡视频在线 | 老司机午夜性大片 | 国产日韩欧美精品一区二区三区 | 成人免费在线视频 | 97国产在线视频 | 免费人成激情视频在线观看冫 | 亚洲福利| 国产91视频免费 |