使用libuv线程池实现Node.js异步函数

来源:互联网 发布:男士休闲皮鞋 知乎 编辑:程序博客网 时间:2024/05/18 22:08

JavaScript是一种单线程的编程语言。在使用Node.js的时候,如果有耗时的操作,需要放到异步函数中。Node.js的底层使用了libuv,用于实现异步I/O。

学习资源

  • Node.js C/C++ Addons
  • An Introduction to libuv
  • Thread pool work scheduling
  • .node-gyp\5.5.0\include\node\uv.h

优化Node.js条形码插件

安装

  • Dynamsoft Barcode Reader
  • Node.js
  • node-gyp:
    npm install -g node-gyp

     

同步接口

先看下同步接口是怎么实现的。

创建dbr.cc,并在里面增加一个函数DecodeFile

#include <node.h>#include <node_buffer.h>#include <string.h>#include <uv.h>#include "If_DBR.h"#include "BarcodeFormat.h"#include "BarcodeStructs.h"#include "ErrorCode.h" using namespace v8; // Barcode formatconst char * GetFormatStr(__int64 format){    if (format == CODE_39)        return "CODE_39";    if (format == CODE_128)        return "CODE_128";    if (format == CODE_93)        return "CODE_93";    if (format == CODABAR)        return "CODABAR";    if (format == ITF)        return "ITF";    if (format == UPC_A)        return "UPC_A";    if (format == UPC_E)        return "UPC_E";    if (format == EAN_13)        return "EAN_13";    if (format == EAN_8)        return "EAN_8";    if (format == INDUSTRIAL_25)        return "INDUSTRIAL_25";    if (format == QR_CODE)        return "QR_CODE";    if (format == PDF417)        return "PDF417";    if (format == DATAMATRIX)        return "DATAMATRIX";     return "UNKNOWN";} /* *  decodeFile(fileName, barcodeTypes, callback) */void DecodeFile(const FunctionCallbackInfo<Value>& args) {     Isolate* isolate = Isolate::GetCurrent();    HandleScope scope(isolate);     // get arguments    String::Utf8Value fileName(args[0]->ToString()); // convert v8 string to char *    char *pFileName = *fileName; // file name    __int64 llFormat = args[1]->IntegerValue();  // barcode types    Local<Function> cb = Local<Function>::Cast(args[2]); // javascript callback function     // initialize Dynamsoft Barcode Reader    int iMaxCount = 0x7FFFFFFF;    ReaderOptions ro = {0};    pBarcodeResultArray pResults = NULL;    ro.llBarcodeFormat = llFormat;    ro.iMaxBarcodesNumPerPage = iMaxCount;     // decode barcode image    int ret = DBR_DecodeFile(pFileName, &ro, &pResults);    if (ret)        printf("Detection error code: %d\n", ret);     int count = pResults->iBarcodeCount;    pBarcodeResult* ppBarcodes = pResults->ppBarcodes;    pBarcodeResult tmp = NULL;     // array for storing barcode results    Local<Array> barcodeResults = Array::New(isolate);     for (int i = 0; i < count; i++)    {        tmp = ppBarcodes[i];         Local<Object> result = Object::New(isolate);        result->Set(String::NewFromUtf8(isolate, "format"), String::NewFromUtf8(isolate, GetFormatStr(tmp->llFormat)));        result->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, tmp->pBarcodeData));        barcodeResults->Set(Number::New(isolate, i), result);    }     // release memory of barcode results    DBR_FreeBarcodeResults(&pResults);     // run the callback    const unsigned argc = 1;    Local<Value> argv[argc] = { barcodeResults };    cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);} void Init(Handle<Object> exports) {    NODE_SET_METHOD(exports, "decodeFile", DecodeFile);} NODE_MODULE(dbr, Init)

现在可以使用JavaScript接口decodeFile了。创建binding.gyp。针对Windows, Linux和macOS添加不同的头文件和库路径:

{  "targets": [    {      'target_name': "dbr",      'sources': [ "dbr.cc" ],      'conditions': [          ['OS=="linux"', {            'defines': [              'LINUX_DBR',            ],            'include_dirs': [                "/home/xiao/Dynamsoft/BarcodeReader4.0/Include"            ],            'libraries': [                "-lDynamsoftBarcodeReaderx64", "-L/home/xiao/Dynamsoft/BarcodeReader4.0/Redist"            ],            'copies': [            {              'destination': 'build/Release/',              'files': [                '/home/xiao/Dynamsoft/BarcodeReader4.0/Redist/libDynamsoftBarcodeReaderx64.so'              ]            }]          }],          ['OS=="win"', {            'defines': [              'WINDOWS_DBR',            ],            'include_dirs': [                "E:\Program Files (x86)\Dynamsoft\Barcode Reader 4.3\Components\C_C++\Include"            ],            'libraries': [                "-lE:\Program Files (x86)\Dynamsoft\Barcode Reader 4.3\Components\C_C++\Lib\DBRx64.lib"            ],            'copies': [            {              'destination': 'build/Release/',              'files': [                'E:\Program Files (x86)\Dynamsoft\Barcode Reader 4.3\Components\C_C++\Redist\DynamsoftBarcodeReaderx64.dll'              ]            }]          }],          ['OS=="mac"', {            'defines': [              'MAC_DBR',            ],            'include_dirs' : [                "/Applications/Dynamsoft/Barcode\ Reader\ 4.1/Include"            ],            'libraries': [                "-lDynamsoftBarcodeReader"            ]          }]      ]    }  ]}

配置构建环境:

node-gyp configure

编译工程:

node-gyp build

创建dbr.js测试下:

var dbr = require('./build/Release/dbr');var readline = require('readline');var fs = require('fs');var barcodeTypes = 0x3FF | 0x2000000 | 0x8000000 | 0x4000000; // 1D, QRCODE, PDF417, DataMatrix function decodeFile(fileName) {    dbr.decodeFile(        fileName, barcodeTypes,        function(msg) {            var result = null;            for (index in msg) {                result = msg[index]                console.log("Format: " + result['format']);                console.log("Value : " + result['value']);                console.log("##################");            }        }    );} var rl = readline.createInterface({    input: process.stdin,    output: process.stdout}); rl.question("Please input a barcode image path: ", function(answer) {    decodeFile(answer);    rl.close();});

异步接口

虽然这样封装接口也是通过回调函数返回的,但是所有的工作都在主线程中,会造成堵塞。解决的方法就是把耗时的工作放到工作线程中。使用接口uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb)可以把任务放到libuv的线程池中。

新建接口decodeFileAsync

/* *  decodeFileAsync(fileName, barcodeTypes, callback) */void DecodeFileAsync(const FunctionCallbackInfo<Value>& args) {    Isolate* isolate = Isolate::GetCurrent();    HandleScope scope(isolate);     // get arguments    String::Utf8Value fileName(args[0]->ToString()); // file name    char *pFileName = *fileName;    __int64 llFormat = args[1]->IntegerValue(); // barcode types    Local<Function> cb = Local<Function>::Cast(args[2]); // javascript callback function     // initialize BarcodeWorker    BarcodeWorker *worker = new BarcodeWorker;    worker->request.data = worker;    strcpy(worker->filename, pFileName);    worker->callback.Reset(isolate, cb);    worker->llFormat = llFormat;    worker->pResults = NULL;    worker->buffer = NULL;         uv_queue_work(uv_default_loop(), &worker->request, (uv_work_cb)DetectionWorking, (uv_after_work_cb)DetectionDone);} void Init(Handle<Object> exports) {    NODE_SET_METHOD(exports, "decodeFile", DecodeFile);    NODE_SET_METHOD(exports, "decodeFileAsync", DecodeFileAsync);}

把条形码的解码识别工作放到uv_work_cb回调函数中:

/* *  uv_work_cb */static void DetectionWorking(uv_work_t *req){    // get the reference to BarcodeWorker    BarcodeWorker *worker = static_cast<BarcodeWorker *>(req->data);     // initialize Dynamsoft Barcode Reader    int iMaxCount = 0x7FFFFFFF;    ReaderOptions ro = {0};    pBarcodeResultArray pResults = NULL;    ro.llBarcodeFormat = worker->llFormat;    ro.iMaxBarcodesNumPerPage = iMaxCount;     // decode barcode image    int ret = 0;    if (worker->buffer)     {        ret = DBR_DecodeStream(worker->buffer, worker->size, &ro, &pResults);    }    else    {        ret = DBR_DecodeFile(worker->filename, &ro, &pResults);    }         if (ret)        printf("Detection error code: %d\n", ret);     // save results to BarcodeWorker    worker->errorCode = ret;    worker->pResults = pResults;}

uv_after_work_cb回调函数用来在主线程显示结果:

/* *  uv_after_work_cb */static void DetectionDone(uv_work_t *req,int status){    Isolate* isolate = Isolate::GetCurrent();    HandleScope scope(isolate);     // get the reference to BarcodeWorker    BarcodeWorker *worker = static_cast<BarcodeWorker *>(req->data);     // get barcode results    pBarcodeResultArray pResults = worker->pResults;    int errorCode = worker->errorCode;    int count = pResults->iBarcodeCount;    pBarcodeResult* ppBarcodes = pResults->ppBarcodes;    pBarcodeResult tmp = NULL;     // array for storing barcode results    Local<Array> barcodeResults = Array::New(isolate);     for (int i = 0; i < count; i++)    {        tmp = ppBarcodes[i];         Local<Object> result = Object::New(isolate);        result->Set(String::NewFromUtf8(isolate, "format"), String::NewFromUtf8(isolate, GetFormatStr(tmp->llFormat)));        result->Set(String::NewFromUtf8(isolate, "value"), String::NewFromUtf8(isolate, tmp->pBarcodeData));        barcodeResults->Set(Number::New(isolate, i), result);    }     // release memory of barcode results    DBR_FreeBarcodeResults(&pResults);     // run the callback    const unsigned argc = 1;    Local<Value> argv[argc] = {barcodeResults};    Local<Function> cb = Local<Function>::New(isolate, worker->callback);    cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);     // release memory of BarcodeWorker    delete worker;}

修改dbr.js调用异步接口:

var dbr = require('./build/Release/dbr');var readline = require('readline');var fs = require('fs');var barcodeTypes = 0x3FF | 0x2000000 | 0x8000000 | 0x4000000; // 1D, QRCODE, PDF417, DataMatrix function decodeFileAsync(fileName) {    dbr.decodeFileAsync(        fileName, barcodeTypes,        function(msg) {            var result = null;            for (index in msg) {                result = msg[index]                console.log("Format: " + result['format']);                console.log("Value : " + result['value']);                console.log("##################");            }        }    );} var rl = readline.createInterface({    input: process.stdin,    output: process.stdout}); rl.question("Please input a barcode image path: ", function(answer) {    decodeFileAsync(answer);    rl.close();});

源码

https://github.com/yushulx/nodejs-barcode-for-win-linux-mac

0 0
原创粉丝点击