在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. The Preliminary Contest for ICPC Asia Xuzhou 2019 K. Center

    这题对于能加入最多边缘点的center点,这个点就是最优的center ,对于center点,总共是n^2的,顶多也就1e6,所以直接双重循环就行了, 然后map<pair,set >映射 ...

  2. java篇 之 继承

    this代表正在使用类的对象(的引用) java支持重载:允许在同一个类中使用相同的方法名(重载类型只区分参数列表,包括参数 顺序,参数个数,参数数据类型,与方法返回类型无关) 匹配: 方法名 参数列 ...

  3. TensorFlow:谷歌图像识别网络inception-v3下载与查看结构

    学习博客: # https://www.cnblogs.com/felixwang2/p/9190731.html # https://www.cnblogs.com/felixwang2/p/919 ...

  4. 基于SILVACO ATLAS的a-IGZO薄膜晶体管二维器件仿真(03)

    今天逛ResearchGate的时候发现了一个不错的Atlas入门教程:Step by step with ATLAS Silvaco点击链接免费下载.. Atlas代码结构 当然可能有一点太基础了. ...

  5. FYF的煎饼果子

    利用等差数列公式就行了,可以考虑特判一下m >= n($ m, n \neq 1 $),这时一定输出“AIYAMAYA”. #include <iostream> using nam ...

  6. JQ - 绑定(on)/解绑(off)事件(浅显的见解)

    on 绑定事件: $("selector").on("click",事件执行函数名); //为 selector 添加 点击事件 $("selecto ...

  7. Java入门笔记 05-多线程

    介绍:Java提供了非常优秀的多线程支持,程序可以通过非常简单的方式来启动多线程.本章主要内容为:多线程的创建.启动.控制以及同步操作,并介绍JDK 5新增的线程创建方式. 一.线程的创建与使用: 1 ...

  8. C:编译过程、目标代码文件、 可执行文件和库

    C编程的基本策略是, 用程序把源代码文件转换为可执行文件(其中包含可直接运行的机器语言代码). 典型的C实现通过编译和链接两个步骤来完成这一过程. 编译器把源代码转换成中间代码, 链接器把中间代码和其 ...

  9. 使用scrapy-redis 搭建分布式爬虫环境

    scrapy-redis 简介 scrapy-redis 是 scrapy 框架基于 redis 数据库的组件,用于 scraoy 项目的分布式开发和部署. 有如下特征: 分布式爬取: 你可以启动多个 ...

  10. node vue 项目git 管理

    push 上传到云的时候,依赖包及相关文件是不上传上去的, 所以每次克隆到本地后,node 项目运行前须要 npm install 安装对应依赖 vue 项目编译前也须要  npm install,安 ...