使用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
- 使用libuv线程池实现Node.js异步函数
- node libuv异步多线程使用初窥
- libuv异步实现分析
- 理解Node.js的事件循环(代码是异步单线程,内部实现用的还是进程和线程,基于池化的线程实现异步)
- node.js 单线程异步理解
- Node.js中同步函数异步编程
- Node.js 7.9.0 异步函数
- libuv 线程池的调度
- Node.js的单线程异步I/O优势
- atl异步线程调用js函数
- node.js异步编程
- Node.js 异步编程
- Node.js 异步编程
- Node.js es6 generator 和 thunk 函数解决异步金字塔
- libuv的线程池,即工作队列
- Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现
- 使用线程池和反射实现异步任务
- 使用线程池与内部类实现方法异步
- 银行常用加密算法PINBlock加密
- Tensorflow设置显存自适应,显存比例
- 大牛的主页
- PL/SQL12.01 32/64位与注册机等分享
- NestedScrollView使用中遇到的一些问题总结
- 使用libuv线程池实现Node.js异步函数
- Leetcode刷题记——83. Remove Duplicates from Sorted List(删除有序链表的重复结点)
- RxJava 入门篇 (二) -- 关键的类
- smmu学习笔记之streamtable 2
- 怎样用sourceTree将自己本地的项目上传到github网站上
- Linux命令学习
- 用JavaScript生成UUID
- Scoket例子
- 《设计模式之禅》读书笔记