在nodejs的官方网站中有关于C++扩展的详细说明,其中包含了从"hello world"到对象封装的一系列示例。其中的“callback”节是关于回调函数的,美中不足的是,这个回调是阻塞的回调。

官方示例的回调函数用JS代码来模拟的话,大致是这个样子:

function syncCallback(callback) {
// 业务代码
// 业务代码
callback();
}

使用C++扩展的一个最大好处就是处理一些CPU密集的业务,因此这部分代码一定是比较耗时的,否则用C++去实现完全没有意义。业务代码中的阻塞操作,例如传统文件读写、密集计算等都会导致nodejs原始线程的阻塞,导致后来的请求无法得到及时响应,严重影响node的并发性能。

有服务器程序开发的朋友肯定已经想到用多线程的方法解决这个问题。是的,我要分享的就是在C++扩展中用多线程的方法处理回调,从而达到解决复杂的业务同时保证node线程的无阻塞特性。

node C++扩展中,可以使用libuv提供的线程方法,非常方便的进行线程调度。

下面是具体代码,详细解释见注释

#include <v8.h>
#include <node.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h> using namespace node;
using namespace v8; //
// 定义线程入参结构体
//
// a: 整型入参1
// b: 整型入参2
// result: 在工作线程里面计算好的a+b
// name: 线程名称,由JS调用代码指定
// callback: 回调函数
struct reqData
{
int result;
int a;
int b;
char name[128];
Persistent<Function> callback;
}; //
// 定义工作线程处理函数
//
// 入参为libuv指定的结构体格式
// 没有返回值
// 具体的业务处理函数
void workerFunc(uv_work_t* req)
{
// 从uv_work_t的结构体中获取我们定义的入参结构
reqData* request = (reqData*)req->data; // 计算结果
request->result = request->a + request->b; // 模拟耗时业务
for(int i = 0; i < 50; ++i) {
// 模拟密集运算导致的阻塞
Sleep(1000);
// 打印每次循环的内容
printf("[%s %04d] I am working.\n", request->name, i);
}
} //
// 定义线程的回调函数
// req: 处理后的数据结构体
// status: 线程状态
void afterWorkFunc(uv_work_t* req, int status)
{
HandleScope scope; // 获取我们定义的数据结构
reqData* request = (reqData*)req->data;
// 释放请求结构体
delete req; // 构造JS回调函数的arguments
Handle<Value> argv[2]; argv[0] = Undefined(); // err
argv[1] = Integer::New(request->result); // data // 下面代码相当于JS中的
// try {
// callback.apply(global, arguments);
// } catch (err) {
// throw err;
// }
TryCatch try_catch;
request->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught())
{
FatalException(try_catch);
} // 回收资源
request->callback.Dispose();
delete request;
} // 定义模块的导出函数
static Handle<Value> test(const Arguments& args)
{
HandleScope scope; // 判断入参是否满足条件
if ( args.Length() < 3 || !args[0]->IsNumber() || !args[1]->IsNumber() )
{
return ThrowException(Exception::TypeError(String::New("Bad argument")));
} // 获取参数,并转化格式
ssize_t int1 ( args[0]->Int32Value() );
ssize_t int2 ( args[1]->Int32Value() );
char nameBuffer[128] = {0};
args[2]->ToString()->WriteAscii(nameBuffer); // 检查回调参数是否为函数
if ( args[3]->IsFunction() )
{
// 构造数据结构
Local<Function> callback = Local<Function>::Cast(args[3]); reqData* request = new reqData;
request->callback = Persistent<Function>::New(callback); request->a = int1;
request->b = int2;
strcpy(request->name, nameBuffer); uv_work_t* req = new uv_work_t();
req->data = request; // 调用libuv的线程处理函数
uv_queue_work(uv_default_loop(), req, workerFunc, afterWorkFunc);
}
else
{
return ThrowException(Exception::TypeError(String::New("Callback missing")));
} return Undefined();
} extern "C"
{
// 相当于JS中的
//
// exports.test = function Test(){};
//
static void init(Handle<Object> target)
{
target->Set(String::NewSymbol("test"), FunctionTemplate::New(test)->GetFunction());
}
} NODE_MODULE(asyncAddon, init);

  

nodejs的C++扩展中实现异步回调的更多相关文章

  1. 如何优雅的处理Nodejs中的异步回调

    前言 Nodejs最大的亮点就在于事件驱动, 非阻塞I/O 模型,这使得Nodejs具有很强的并发处理能力,非常适合编写网络应用.在Nodejs中大部分的I/O操作几乎都是异步的,也就是我们处理I/O ...

  2. java 中的异步回调

    异步回调,本来在c#中是一件极为简单和优雅的事情,想不到在java的世界里,却如此烦琐,先看下类图: 先定义了一个CallBackTask,做为外层的面子工程,其主要工作为start 开始一个异步操作 ...

  3. ArcGIS中使用异步回调函数查询图层Graphic

    在我们的地图的操作中经常会有一些操作是需要通过画多边形或者画线来查找某一块区域内的特定的Graphics比如我们在做的交警的项目中通过框选来查找某一块区域中的摄像机,某一块区域中的警力.警情.警员等相 ...

  4. Android Binder机制中的异步回调

    “Binder通信是同步而不是异步的”,但是在实际使用时,是设计成客户端同步而服务端异步. 看看Framwork层的各service类java源码便会知道,在客户端调用服务端的各种方法时,通常会传递一 ...

  5. nodejs中的异步回调机制

    1.再次clear Timer定时器的作用 setTimeOut绝非是传统意义上的“sleep”功能,它做不到让主线程“熄火”指定时间,它是用来指定:某个回调在固定时间后插入执行栈!(实际执行时间略长 ...

  6. day37 异步回调和协程

    异步回调 """ 异步任务使用场景 爬虫 1.从目标站点下载网页数据 本质就是HTML格式字符串 2.用re从字符串中提取出你需要的数据 ""&quo ...

  7. 并发编程 —— 自己写一个异步回调 API

    1. 前言 在并发编程中,异步回调的效率不言而喻,在业务开发中,如果由阻塞的任务需要执行,必然要使用异步线程.并且,如果我们想在异步执行之后,根据他的结果执行一些动作. JDK 8 之前的 Futur ...

  8. C#基础:线程之异步回调(委托)

    异步回调,什么是异步回调?我是这样理解的,当主线程在执行一段代码的时候,我们用委托执行了一个线程,这个线程要返回一个结果,关键是什么时候返回这个结果,异步回调就是在这个线程执行完成后立即返回这个线程的 ...

  9. 深入理解nodejs中的异步编程

    目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...

随机推荐

  1. HTML连载59-子绝父相

    一.子绝父相 1.只使用相对定位,对图片的位置进行精准定位. <!DOCTYPE html> <html lang="en"> <head> & ...

  2. Ubuntu 国内安装 kubernetes

    由于墙的原因,国内要安装 kubernetes 非常的麻烦,因此只要解决这个问题,就可以顺利安装 kubernetes 的 三个官法工具 kubelet.kubeadm.kubectl. 安装环境: ...

  3. Spring Boot 操作 Excel

    Excel 在日常操作中经常使用到,Spring Boot 中使用 POI 操作 Excel 本项目源码 github 下载 1 新建 Spring Boot Maven 示例工程项目 注意:本示例是 ...

  4. ${var}与$var的区别,个人总结的看法

    ${var}  set variable 123 ${var}类似于变量值,相当于123 $var类似于变量名,指向对应的存储位置,相当于“123”,一般只有在rf关键字为断言的如“should be ...

  5. VMware安装ACS5.8

    1.所需组件: VMware ACS5.8 iso 2.安装要求: 2 Core Processor 4 GB RAM 60 GB Hard drive 这些要求算是比较低的要求,不能比这个更low了 ...

  6. jdk 9 10 11 12 13 新特性

    jdk 9 新特性 1.集合加强 jdk9 为所有集合(List/Set/Map)都增加了 of 和 copyOf 方法,用来创建不可变集合,即一旦创建就无法再执行添加.删除.替换.排序等操作,否则将 ...

  7. ztree-拖拽(排序树)

    <!DOCTYPE html> <HTML> <HEAD> <TITLE> ZTREE DEMO - beforeDrag / onDrag / bef ...

  8. PHP 站内消息的表结构设计

    1)添加全站通知:信息存入到 tb_message 2)用户点开信息或者设置信息为已阅读:插入记录到 tb_message_readlog 如何设计存储的表?求最佳方案 CREATE TABLE `m ...

  9. JZOJ5915 [2018NOIP模拟] 明日之星(广义后缀自动机,线段树)

    题目描述 给定一棵树,每个节点有一个权值 \(a_i\) 和一个字符串 \(s_i\). q组询问,每次询问一个字符串 S 和两个节点x,y: 求x到y路径上每个节点的字符串在 S 中出现的次数乘上各 ...

  10. Centos610安装Jdk1.8

    1.下载JDK 下载:  https://pan.baidu.com/s/15TYsE_wfSb48pS4SpUQKHA 提取码:fsx6 2.上传安装包 上传到linux 并拷贝到/opt/jdk目 ...