nodejs的C++扩展中实现异步回调
在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++扩展中实现异步回调的更多相关文章
- 如何优雅的处理Nodejs中的异步回调
前言 Nodejs最大的亮点就在于事件驱动, 非阻塞I/O 模型,这使得Nodejs具有很强的并发处理能力,非常适合编写网络应用.在Nodejs中大部分的I/O操作几乎都是异步的,也就是我们处理I/O ...
- java 中的异步回调
异步回调,本来在c#中是一件极为简单和优雅的事情,想不到在java的世界里,却如此烦琐,先看下类图: 先定义了一个CallBackTask,做为外层的面子工程,其主要工作为start 开始一个异步操作 ...
- ArcGIS中使用异步回调函数查询图层Graphic
在我们的地图的操作中经常会有一些操作是需要通过画多边形或者画线来查找某一块区域内的特定的Graphics比如我们在做的交警的项目中通过框选来查找某一块区域中的摄像机,某一块区域中的警力.警情.警员等相 ...
- Android Binder机制中的异步回调
“Binder通信是同步而不是异步的”,但是在实际使用时,是设计成客户端同步而服务端异步. 看看Framwork层的各service类java源码便会知道,在客户端调用服务端的各种方法时,通常会传递一 ...
- nodejs中的异步回调机制
1.再次clear Timer定时器的作用 setTimeOut绝非是传统意义上的“sleep”功能,它做不到让主线程“熄火”指定时间,它是用来指定:某个回调在固定时间后插入执行栈!(实际执行时间略长 ...
- day37 异步回调和协程
异步回调 """ 异步任务使用场景 爬虫 1.从目标站点下载网页数据 本质就是HTML格式字符串 2.用re从字符串中提取出你需要的数据 ""&quo ...
- 并发编程 —— 自己写一个异步回调 API
1. 前言 在并发编程中,异步回调的效率不言而喻,在业务开发中,如果由阻塞的任务需要执行,必然要使用异步线程.并且,如果我们想在异步执行之后,根据他的结果执行一些动作. JDK 8 之前的 Futur ...
- C#基础:线程之异步回调(委托)
异步回调,什么是异步回调?我是这样理解的,当主线程在执行一段代码的时候,我们用委托执行了一个线程,这个线程要返回一个结果,关键是什么时候返回这个结果,异步回调就是在这个线程执行完成后立即返回这个线程的 ...
- 深入理解nodejs中的异步编程
目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...
随机推荐
- Vue - 如何使用npm run build后的dist文件夹
脚手架vue cli生成项目后,使用 npm run build 生成了一个dist文件夹(应该是distribution的缩写) 只要放在http服务器上就可以运行. 使用一句python命令可以搭 ...
- Django - 在settings配置终端打印SQL语句
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'level': 'DE ...
- 2、介绍在TensorFlow当中使用不同的方式创建张量tensor
import tensorflow as tf from tensorflow.python.framework import ops ops.reset_default_graph() #开始一个计 ...
- The Preliminary Contest for ICPC Asia Xuzhou 2019 E XKC's basketball team(排序+二分)
这题其实就是瞎搞,稍微想一想改一改就能过. 排序按值的大小排序,之后从后向前更新node节点的loc值,如果后一个节点的loc大于(不会等于)前一个节点的loc,就把前一个节点的loc值设置为后面的l ...
- 201771010135 杨蓉庆《2018面向对象程序设计(java)课程学习进度条》
...
- php虚拟主机下实现定时任务(仅供参考)
因为要做简单的中控 在实现心跳包的时候遇到了困难 正常的心跳包思路是这样的 举个例子 我写一个登陆签到脚本 当我登陆成功的时候 会把登陆成功这个状态传递给网络上的中控端 当我签到完成的时候会把 ...
- Git主库私库相关操作操作
命令1: git remote add 库名称 库地址 说明:写好的代码提交到两个git远端,git remote add是将另一个库地址设置进来 命令2: git fetch 库名称 分支名称 说明 ...
- php学习之始于html——div布局与css控制
关于您的问题:xampp是一个集成的php开发环境,里面包含Apache,mysql等环境,主要充当一个服务器的角色, 其中有文件,数据,路径等,一个网站程序安装之后,都会有一个根目录,根目录下,有其 ...
- python 中的 int() 与 round
int(x):向下取整 round(x):超过 .5 则向上取整,否则向下取整
- 《Web安全攻防 渗透测试实战指南》 学习笔记(一)
Web安全攻防 渗透测试实战指南 学习笔记 (一) 第一章 信息收集 在信息收集中,最重要是收集服务器的配置信息和网站敏感信息(域名及子域名信息目标网站系统.CMS指纹.目标网站真实I ...