文档章节

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

yushulx
 yushulx
发布于 2017/02/28 14:59
字数 1184
阅读 188
收藏 0

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

学习资源

优化Node.js条形码插件

安装

同步接口

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

创建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 format
const 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

© 著作权归作者所有

共有 人打赏支持
yushulx
粉丝 25
博文 88
码字总数 49460
作品 0
杭州
《Node.js》之事件处理机制以及事件环机制(二)

传统服务器与Node.js的比较 传统的服务器在面临高并发的场景时,会使用多线程方案,服务器会为客户端的请求分配一个线程,使用同步的I/O,系统通过线程切换来弥补同步I/O调用过程中的时间开销...

同Young不同样
08/08
0
0
Javascript 异步实现机制

Javascript 单线程指的是在一个浏览器进程中只存在一个 Javascript 执行线程,所以任务需要顺序排列等待执行,而不能像 Java 等多线程语言一样并发执行。但是这种单线程模型在处理耗时的异步...

木头先生
2017/12/11
0
0
深入分析Node.js事件循环与消息队列

多数的网站不需要大量计算,程序花费的时间主要集中在磁盘 I/O 和网络 I/O 上面 SSD读取很快,但和CPU处理指令的速度比起来也不在一个数量级上,而且网络上一个数据包来回的时间更慢: 一个数...

小平果118
08/03
0
0
深入理解Node.js:核心思想与源码分析【1】

标签: node 源码 学习 源码: nianniuer node背景,了解一下 (1)体系架构 Node.js主要分为四大部分,Node Standard Library,Node Bindings,V8,Libuv,架构图如下: Node Standard Libra...

张倩1488543336000
07/02
0
0
libuv 概要及其使用时需要注意的一些问题

libuv socket asio network 1.介绍说明 libuv 是一个开源的跨平台的基于C的完全异步的网络库:https://github.com/libuv/libuv 关于这个库的优点及说明的视频: http://v.youku.com/vshow/i...

星罗棋布
2015/03/20
0
9

没有更多内容

加载失败,请刷新页面

加载更多

下一页

jetbrains系产品IDEA:mac上面提示快捷键设置

原因 由于Mac上面的Ctrl+空格变成输入法切换的快捷键,在使用IDEA的过程中,代码提示很不方便,需要使用option+/这种传统eclipse上面的代码提示快捷键作为主要快捷键。 怎么修改? 移除【opt...

亚林瓜子
32分钟前
0
0
Exclipse 输出结果时换行

System.out.println(f1 + "\n" + d1 + "\n" + d2);

笑丶笑
33分钟前
1
0
怎样治疗标签不能触发onblur事件

I realize this was over a year ago, but it showed up for me in Google while trying to solve this same issue. It seems Chrome does not consider some elements, like body and ancho......

Weijuer
36分钟前
0
0
vue常见库安装

移动设备上的浏览器默认会在用户点击屏幕大约延迟300毫秒后才会触发点击事件,这是为了检查用户是否在做双击。为了能够立即响应用户的点击事件,才有了FastClick。 安装fastclick npm insta...

林夏夕
38分钟前
0
0
kafka 教程(三) kafka Java API 编程

下午写

MrPei
39分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部