文档章节

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

yushulx
 yushulx
发布于 2017/02/28 14:59
字数 1184
阅读 194
收藏 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
粉丝 26
博文 92
码字总数 52955
作品 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

没有更多内容

加载失败,请刷新页面

加载更多

《Netkiller Java 手札》· 二进制文件操作大全

本文节选自《Netkiller Java 手札》 Netkiller Java 手札 Mr. Neo Chan, 陈景峯(BG7NYT) 中国广东省深圳市望海路半岛城邦三期 518067 +86 13113668890 <netkiller@msn.com> $Id: book.xml 6......

netkiller-
18分钟前
1
0
Fiddler Debugger post请求

常用的两种: 第一种默认的 对应URL为www 的要用请求头为:Content-Type: application/x-www-form-urlencoded 请求参数为 :param1=1234¶m2=12345 注:有些接口是指定用这种的第二方式并不...

轻量级赤影
25分钟前
2
0
如何搭建母婴亲子类知识社区

近期社交领域融资动作频繁,海尔高管、海尔医疗有限公司总裁管礼庆创办的母婴知识分享社区平台Alwayslove于上月获得700万天使轮融资。 Alwayslove是一个母婴知识分享社区平台,采用UGC模式,...

ThinkSNS账号
27分钟前
1
0
Android 自定义构建类型 BuildType

最近接触到自定义构建类型 BuildType,发现这一块有些地方稍不注意的话会被绕进去浪费点时间,既然我这边已经花费时间了,如果正好你也需要接触到 BuildType,也许接下来分享的 tips 可能会帮...

猴亮屏
28分钟前
1
0
美团点评基于 Flink 的实时数仓建设实践

引言 近些年,企业对数据服务实时化服务的需求日益增多。本文整理了常见实时数据组件的性能特点和适用场景,介绍了美团如何通过 Flink 引擎构建实时数据仓库,从而提供高效、稳健的实时数据服...

美团技术团队
31分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部