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 ...
随机推荐
- STM32F103之GPIO学习记录
9 GPIO:通用I/O口 AFIO:可供选择的I/O口 9.1 GPIO功能描述 每一个通用的I/O口都有: 两个32位的配置寄存器(配置寄存器低和配置寄存器高): 两个32位的数据寄存器(输入数据 ...
- vue中使用vue-i18n 一个简单的国际化操作
1.安装:npm install vue-i18n --save-dev 2.在main.js文件中引入: import VueI18n from 'vue-i18n' Vue.use(VueI18n ...
- TOMCAT中文信息乱码改为GBK
# Licensed to the Apache Software Foundation (ASF) under one or more# contributor license agreements ...
- 1、MyBatis框架底层初涉
1.拜年 哈哈,现在是过年了,祝大家新年好. 本来大过年的是不打算碰电脑的,(抢票除外,三疯同学现在还没抢到票,然后突然又延长假期了).现在疫情严重,被堵家里不能出去了.不能为国家做贡献,但是起码不能 ...
- elasticsearch ik解析器
ik解析器 1. ik解析器 The IK Analysis plugin integrates Lucene IK analyzer (http://code.google.com/p/i ...
- matplotlib学习(2)
1.legend的学习(图例)1.1 代码 import matplotlib.pyplot as plt import numpy as np x=np.linspace(-3,3,50) #从-1 ...
- 三 模拟实现顺序表ArrayList
/** * 顺序表,重点是数组动态扩容,插入 * 底层采用数组,长度可以动态变化,此处采用增长一倍 * java.util.ArrayList每次增长50% * int newCapacity = ...
- Java学习资源 - J2EE
java Web开发基础(一)工程项目文档结构 ========rmi=========== Java RMI 框架(远程方法调用) java RMI原理详解 深究Java中的RMI底层原理 ==== ...
- docker+hexo 搭建博客
前提 Linux服务器 保证自己服务器上的端口对外开放,即设置相应的防火墙规则 安装好hexo 安装:npm install hexo-cli -g 初始化搭建:npm init myBlog,myB ...
- 【JS 日期】获取当前日期时间
获取当前日期时间 <!DOCTYPE html> <html> <head></head> <body> <script> wi ...