文档章节

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

yushulx
 yushulx
发布于 2017/02/28 14:59
字数 1184
阅读 181
收藏 0
点赞 0
评论 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
博文 85
码字总数 47908
作品 0
杭州
浅谈Node.js单线程模型

Node.js采用 事件驱动 和 异步I/O 的方式,实现了一个单线程、高并发的运行时环境,而单线程就意味着同一时间只能做一件事,那么Node.js如何利用单线程来实现高并发和异步I/O?本文将围绕这个...

wudized ⋅ 2017/07/10 ⋅ 0

深入浅出Node.js(五):初探Node.js的异步I/O实现

异步I/O 在操作系统中,程序运行的空间分为内核空间和用户空间。我们常常提起的异步I/O,其实质是用户空间中的程序不用依赖内核空间中的I/O操作实际完成,即可进行后续任务。以下伪代码模仿了...

leeldy ⋅ 2012/10/25 ⋅ 0

libuv 概要及其使用时需要注意的一些问题

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

星罗棋布 ⋅ 2015/03/20 ⋅ 9

关于真正理解Node.js事件循环你需要了解的一切

摘要:Node.js是一个基于事件的平台。这意味着Node中发生的任何事情都是对于事件的响应。传入Node的数据处理要经历一层层嵌套的回调。这一流程相对于开发者被抽象出来,由一个叫做libuv的库处...

众成翻译 ⋅ 01/03 ⋅ 0

每日科技英文47: 微软的多线程,多任务力作Napa.js库

今日要点: 微软napa.js让google v8 js引擎具有多线程多任务运行功能 nodejs基于v8引擎,因此napa.js也可以适配到nodejs上 napa.js核心代码都是使用c++11标准撰写,代码精心设计,编写优美,代...

随风而行之青衫磊落险峰行 ⋅ 2017/12/17 ⋅ 0

浏览器说:虽然都叫event loop,但是我和node不一样

讨论event loop要做到以下两点 首先要确定好上下文,nodejs和浏览器的event loop是两个有明确区分的事物,不能混为一谈。 其次,讨论一些js异步代码的执行顺序时候,要基于node的源码而不是自...

我是家碧 ⋅ 06/04 ⋅ 0

Libuv 1.16.1 发布,Node 的跨平台异步 IO 库

Libuv 1.16.1 已发布, 更新如下: unix: move net/if.h include win: fix undeclared NDISIFMAXSTRINGSIZE Libuv 是一个专注于异步 I/O 的多平台支持库,主要用于 Node.js。 特性包括: 非阻...

王练 ⋅ 2017/11/12 ⋅ 3

Node.js 线程你理解的可能是错的

Node.js是单线程的,那么Node.js启动后线程数是1? 答案:Node.js启动后线程数并不是1,以下面代码为例 通过Mac实用工具 > 活动监视器可以查看进程的线程数其实是6 Node.js启动的线程数不为1...

Randal ⋅ 06/11 ⋅ 0

Node.js 异步异闻录

本文首发在个人博客:http://muyunyun.cn/posts/7b9fdc87/ 提到 Node.js, 我们脑海就会浮现异步、非阻塞、单线程等关键词,进一步我们还会想到 buffer、模块机制、事件循环、进程、V8、libuv...

牧云云 ⋅ 2017/09/22 ⋅ 0

【深入浅出Node.js系列五】初探Node.js的异步I/O实现

深入浅出Node.js系列 【深入浅出Node.js系列一】什么是Node.js 【深入浅出Node.js系列二】Node.js&NPM的安装与配置 【深入浅出Node.js系列三】深入Node.js的模块机制 【深入浅出Node.js系列四...

陶邦仁 ⋅ 2016/01/11 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

到底会改名吗?微软GVFS 改名之争

微软去年透露了 Git Virtual File System(GVFS)项目,GVFS 是 Git 版本控制系统的一个开源插件,允许 Git 处理 TB 规模的代码库,比如 270 GB 的 Windows 代码库。该项目公布之初就引发了争...

linux-tao ⋅ 32分钟前 ⋅ 0

笔试题之Java基础部分【简】【二】

1.静态变量和实例变量的区别 在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变...

anlve ⋅ 49分钟前 ⋅ 0

Lombok简单介绍及使用

官网 通过简单注解来精简代码达到消除冗长代码的目的 优点 提高编程效率 使代码更简洁 消除冗长代码 避免修改字段名字时忘记修改方法名 4.idea中安装lombnok pom.xml引入 <dependency> <grou...

to_ln ⋅ 今天 ⋅ 0

【转】JS浮点数运算Bug的解决办法

37.5*5.5=206.08 (JS算出来是这样的一个结果,我四舍五入取两位小数) 我先怀疑是四舍五入的问题,就直接用JS算了一个结果为:206.08499999999998 怎么会这样,两个只有一位小数的数字相乘,怎...

NickSoki ⋅ 今天 ⋅ 0

table eg

user_id user_name full_name 1 zhangsan 张三 2 lisi 李四 `` ™ [========] 2018-06-18 09:42:06 星期一½ gdsgagagagdsgasgagadsgdasgagsa...

qwfys ⋅ 今天 ⋅ 0

一个有趣的Java问题

先来看看源码: public class TestDemo { public static void main(String[] args) { Integer a = 10; Integer b = 20; swap(a, b); System.out......

linxyz ⋅ 今天 ⋅ 0

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 今天 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 快别开心了,你还没有女友呢。

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享吴彤的单曲《好春光》 《好春光》- 吴彤 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :小萝莉街上乱跑,误把我认错成...

小小编辑 ⋅ 今天 ⋅ 9

Java 开发者不容错过的 12 种高效工具

Java 开发者常常都会想办法如何更快地编写 Java 代码,让编程变得更加轻松。目前,市面上涌现出越来越多的高效编程工具。所以,以下总结了一系列工具列表,其中包含了大多数开发人员已经使用...

jason_kiss ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部