自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

通過N-API使用Libuv線程池

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

 [[403810]]

本文轉(zhuǎn)載自微信公眾號(hào)「編程雜技」,作者theanarkh。轉(zhuǎn)載本文請聯(lián)系編程雜技公眾號(hào)。

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

1 子進(jìn)程

2 子線程

3 Libuv線程池

前兩種是開發(fā)效率比較高的,因?yàn)槲覀冎恍枰獙慾s。但是也有些缺點(diǎn)

1 執(zhí)行js的成本

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

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

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

  1. napi_create_async_work // 創(chuàng)建一個(gè)worr,但是還沒有執(zhí)行 
  2. napi_delete_async_work // 釋放上面創(chuàng)建的work的內(nèi)存 
  3. napi_queue_async_work // 往Libuv提交一個(gè)work 
  4. napi_cancel_async_work // 取消Libuv中的任務(wù),如果已經(jīng)在執(zhí)行則無法取消 

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

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

js提交一個(gè)任務(wù),然后傳入一個(gè)回調(diào)。接著看看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) 

首先定義導(dǎo)出的函數(shù),接著看核心邏輯。

1 定義一個(gè)結(jié)構(gòu)體保存上下文

  1. struct info 
  2.   int sum; // 保存計(jì)算結(jié)果 
  3.   napi_ref func; // 保存回調(diào) 
  4.   napi_async_work worker; // 保存work對(duì)象 
  5. }; 

2 提交任務(wù)到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.   // 創(chuàng)建一個(gè)work,ptr保存的上下文會(huì)在work函數(shù)和done函數(shù)里使用 
  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; 

執(zhí)行上面的函數(shù),任務(wù)就會(huì)被提交到Libuv線程池了。

3 Libuv子線程執(zhí)行任務(wù)

  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

很簡單,計(jì)算幾個(gè)數(shù)。并且保存結(jié)果。

4 回調(diào)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.     // 拿到結(jié)果 
  12.     napi_create_int32(env, arg->sum, &sum); 
  13.     napi_get_reference_value(env, arg->func, &callback); 
  14.     napi_get_global(env, &global); 
  15.     // 回調(diào)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.   } 

并且執(zhí)行后,我們看到輸出了45。接下來我們分析大致的過程。首先我呢看看ThreadPoolWork,ThreadPoolWork是對(duì)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。我們看看每個(gè)函數(shù)的意義。DoThreadPoolWork和AfterThreadPoolWork是虛函數(shù),由子類實(shí)現(xiàn),我們一會(huì)看子類的時(shí)候再分析。我們看看ScheduleWork

  1. void ThreadPoolWork::ScheduleWork() { 
  2.   env_->IncreaseWaitingRequestCounter(); 
  3.   int status = uv_queue_work( 
  4.       env_->event_loop(), 
  5.       &work_req_, 
  6.       // Libuv子線程里執(zhí)行的任務(wù)函數(shù) 
  7.       [](uv_work_t* req) { 
  8.         ThreadPoolWork* self = ContainerOf(&ThreadPoolWork::work_req_, req); 
  9.         self->DoThreadPoolWork(); 
  10.       }, 
  11.       // 任務(wù)處理完后的回調(diào) 
  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是負(fù)責(zé)給Libuv提交任務(wù)的函數(shù)。接著看看CancelWork。

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

直接調(diào)用Libuv的函數(shù)取消任務(wù)??赐旮割?,我們看看子類的定義,子類在N-API里實(shí)現(xiàn)。

  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.   // 釋放該類對(duì)象的內(nèi)存 
  32.   static void Delete(Workwork) { 
  33.     delete work
  34.   } 
  35.   // 執(zhí)行用戶設(shè)置的函數(shù) 
  36.   void DoThreadPoolWork() override { 
  37.     _execute(_env, _data); 
  38.   } 
  39.  
  40.   void AfterThreadPoolWork(int status) override { 
  41.    // 執(zhí)行用戶設(shè)置的回調(diào) 
  42.     _complete(env, ConvertUVErrorCode(status), _data); 
  43.   } 
  44.  
  45.  private: 
  46.   node_napi_env _env; 
  47.   // 用戶設(shè)置的數(shù)據(jù),用于保存執(zhí)行結(jié)果等 
  48.   void* _data; 
  49.   // 執(zhí)行任務(wù)的函數(shù) 
  50.   napi_async_execute_callback _execute; 
  51.   // 任務(wù)處理完的回調(diào) 
  52.   napi_async_complete_callback _complete; 
  53. }; 

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

  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本質(zhì)上是對(duì)Work的簡單封裝,創(chuàng)建一個(gè)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用于任務(wù)執(zhí)行完后釋放Work對(duì)應(yīng)的內(nèi)存。

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是對(duì)ScheduleWork的封裝,作用是給Libuv線程池提交任務(wù)。

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是對(duì)CancelWork的封裝,即取消Libuv線程池的任務(wù)。我們看到一層層套,沒有太多邏輯,主要是要符合N-API的規(guī)范。

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

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

 

責(zé)任編輯:武曉燕 來源: 編程雜技
相關(guān)推薦

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模式

2023-06-08 07:48:03

Java線程池

2021-02-01 08:28:24

Linux線程池Linux系統(tǒng)

2020-04-29 14:10:44

Java線程池編程語言

2023-05-19 08:01:24

Key消費(fèi)場景

2024-05-21 11:09:17

2024-07-15 08:20:24

2023-10-12 08:29:06

線程池Java

2021-07-11 23:25:29

Libuvepoll文件

2015-03-24 16:29:55

默認(rèn)線程池java

2024-11-27 08:15:50

2014-12-24 10:00:07

Spring

2021-06-06 23:40:53

線程池使用場景

2012-05-15 02:18:31

Java線程池

2020-12-10 08:24:40

線程池線程方法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)