node libuv异步多线程使用初窥

5550阅读 0评论2014-01-02 ygfinsight
分类:C/C++

libuv介绍:
libuv 是node.js作者写的,是 Node 的新跨平台抽象层,用于抽象 Windows 的 IOCP 及 Unix 的 libev。作者打算在这个库的包含所有平台的差异性。 
特性: ibuv 严格使用 异步 、 事件驱动 的编程风格。其核心工作是提供事件循环及基于 I/O 或其他活动事件的回调机制,非阻塞 TCP 套接字 非阻塞命名管道 UDP 定时器 子进程生成 通过 uv_getaddrinfo 实现异步 DNS 异步文件系统 API:uv_fs_* 高分辨率时间:uv_hrtime 正在运行程序路径查找:uv_exepath 线程池调度:uv_queue_work TTY控制的ANSI转义代码: uv_tty_t 文件系统事件现在支持 inotify, ReadDirectoryChangesW 和 kqueue。很快会支持事件端口:uv_fs_event_t 进程间的 IPC 与套接字共享:uv_write2 
自己写的一个小例子:
client.h

点击(此处)折叠或打开

  1. #ifndef CLIENTTEST_H_
  2. #define CLIENTTEST_H_

  3. #include <v8.h>
  4. #include <node.h>
  5. #include <node_object_wrap.h>

  6. using namespace node;
  7. using namespace v8;

  8. class ClientTest : ObjectWrap {
  9. public:
  10.     ClientTest(){}
  11.     ~ClientTest(){}
  12.     //构造并初始化对外的函数或方法给js使用
  13.     static void Init(Handle<Object> target);
  14.     //对外的构造对象的New函数
  15.     static Handle<Value> New(const Arguments& args);
  16.     //对外的异步方法
  17.     static Handle<Value> connect(const Arguments& args);
  18.     //异步工作函数
  19.     static void connectWork(uv_work_t* req);
  20.     //异步工作函数结束之后调用的函数
  21.     static void afterConnect(uv_work_t* req, int status);
  22.     //测试函数
  23.     static Handle<Value> test(const Arguments& args);

  24.     //获取构造函数,需要自己释放
  25.     static Persistent<FunctionTemplate> constructorTemplate;

  26. };

  27. #endif
baton.h

点击(此处)折叠或打开

  1. #ifndef BATON_H_
  2. #define BATON_H_
  3. #include <node.h>
  4. #include <v8.h>
  5. #include <uv.h>
  6. #include <string>
  7. #include "client.h"

  8. using namespace v8;

  9. class Baton{
  10. public:
  11.     Baton(ClientTest*c,Handle<Function>*callback);
  12.     ~Baton();
  13.     /*存放回调函数,使用persistent来声明,让程序不会在函数结束后自动回收
  14.      当回调成功后,使用discope释放空间
  15.     */
  16.     Persistent<Function> callback;
  17.     
  18.     
  19.     //错误信息
  20.     bool err;
  21.     std::string *errMsg;
  22.     //运行需要的参数
  23.     ClientTest *client;
  24.     std::string hostname;
  25.     std::string user;
  26.     std::string password;


  27. };
  28. #endif
utils.h(第三方提供)

点击(此处)折叠或打开

  1. #ifndef _util_h_
  2. #define _util_h_

  3. #include <string>
  4. #include <sstream>
  5. #include <stdio.h>
  6. #include <stdlib.h>

  7. //判断参数是否是bool类型,如果不是,做异常处理
  8. #define REQ_BOOL_ARG(I, VAR) \
  9.     if (args.Length() <= (I) || !args[I]->IsBoolean()) \
  10.     return ThrowException(Exception::TypeError(String::New("Argument " #I " must be a bool"))); \
  11.     bool VAR = args[I]->IsTrue();
  12. //判断参数是否是string类型,并作转化
  13. #define REQ_STRING_ARG(I, VAR) \
  14.     if (args.Length() <= (I) || !args[I]->IsString()) \
  15.     return ThrowException(Exception::TypeError(String::New("Argument " #I " must be a string"))); \
  16.     Local<String> VAR = Local<String>::Cast(args[I]);
  17. //判断参数是否是数组类型
  18. #define REQ_ARRAY_ARG(I, VAR) \
  19.     if (args.Length() <= (I) || !args[I]->IsArray()) \
  20.     return ThrowException(Exception::TypeError(String::New("Argument " #I " must be an array"))); \
  21.     Local<Array> VAR = Local<Array>::Cast(args[I]);
  22. //判断参数是否是函数类型
  23. #define REQ_FUN_ARG(I, VAR) \
  24.     if (args.Length() <= (I) || !args[I]->IsFunction()) \
  25.     return ThrowException(Exception::TypeError(String::New("Argument " #I " must be a function"))); \
  26.     Local<Function> VAR = Local<Function>::Cast(args[I]);
  27. //判断参数是否是对象类型并将对象转化为Local<Object>
  28. #define REQ_OBJECT_ARG(I, VAR) \
  29.     if (args.Length() <= (I) || !args[I]->IsObject()) \
  30.     return ThrowException(Exception::TypeError(String::New("Argument " #I " must be an object"))); \
  31.     Local<Object> VAR = Local<Object>::Cast(args[I]);
  32. //给对象的某一字符串变量赋值var是变量,key是用来赋值的关键字
  33. #define OBJ_GET_STRING(OBJ, KEY, VAR) \
  34.   { \
  35.   Local<Value> __val = OBJ->Get(String::New(KEY)); \
  36.   if(__val->IsString()) { \
  37.   String::Utf8Value __utf8Val(__val); \
  38.   VAR = *__utf8Val; \
  39.   } \
  40.   }

  41. //给对象的某一数值类型赋值
  42. #define OBJ_GET_NUMBER(OBJ, KEY, VAR, DEFAULT) \
  43.   { \
  44.   Local<Value> __val = OBJ->Get(String::New(KEY)); \
  45.   if(__val->IsNumber()) { \
  46.   VAR = __val->ToNumber()->Value(); \
  47.   } \
  48.     else if(__val->IsString()) { \
  49.     String::Utf8Value __utf8Value(__val); \
  50.     VAR = atoi(*__utf8Value); \
  51.     } else { \
  52.     VAR = DEFAULT; \
  53.     } \
  54.   }


  55. #endif
client.cpp

点击(此处)折叠或打开

  1. #include <iostream>
  2. #include "client.h"
  3. #include "baton.h"
  4. #include "utils.h"

  5. Persistent<FunctionTemplate> ClientTest::constructorTemplate;
  6. void ClientTest::Init(Handle<Object> target)
  7. {
  8.     HandleScope scope;

  9.     //定义一个函数模板
  10.     Local<FunctionTemplate> t = FunctionTemplate::New(New);
  11.     constructorTemplate = Persistent<FunctionTemplate>::New(t);
  12.     constructorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
  13.     constructorTemplate->SetClassName(String::NewSymbol("ClientTestTest"));

  14.     NODE_SET_PROTOTYPE_METHOD(constructorTemplate,"connection",connect);
  15.     NODE_SET_PROTOTYPE_METHOD(constructorTemplate,"test",test);
  16.     target->Set(String::NewSymbol("ClientTest"),constructorTemplate->GetFunction());
  17. }

  18. Handle<Value> ClientTest::New(const Arguments& args)
  19. {
  20.     HandleScope scope;
  21.     ClientTest *client = new ClientTest();//实例化一个Client对象,用指针client指向
  22.     client->Wrap(args.This()); //包裹client对象
  23.     return scope.Close(args.This());//返回这个对象给外部使用
  24. }

  25. Handle<Value> ClientTest::connect(const Arguments& args)
  26. {
  27.     HandleScope scope;
  28.     //把第一个参数js对象转换成C++可用的对象
  29.     REQ_OBJECT_ARG(0, settings);
  30.     //回调函数的判断并转化
  31.     REQ_FUN_ARG(1, callback);
  32.     //将node.js的对象包装成C++对象
  33.     ClientTest* client = ObjectWrap::Unwrap<ClientTest>(args.This());
  34.     //构建一个baton 对象
  35.     Baton* baton = new Baton(client, &callback);
  36.     //获取对象字符串并赋值
  37.     OBJ_GET_STRING(settings, "hostname", baton->hostname);
  38.     OBJ_GET_STRING(settings, "user", baton->user);
  39.     OBJ_GET_STRING(settings, "password", baton->password);

  40.     client->Ref();
  41.     uv_work_t* req = new uv_work_t();
  42.     req->data = baton;
  43.     //将任务加入到工作队列
  44.     uv_queue_work(uv_default_loop(), req, connectWork, (uv_after_work_cb)afterConnect);
  45.     //返回
  46.     return scope.Close(Undefined());

  47. }
  48. void ClientTest::connectWork(uv_work_t* req)
  49. {
  50.     //Baton 类型指针做转化
  51.     Baton *baton = static_cast<Baton*>(req->data);
  52.     baton->errMsg = NULL;
  53.     baton->err = false;
  54.     //模拟工作,这里省去了异常处理
  55.     std::cout<<"I am connecting!"<<std::endl;
  56. }

  57. void ClientTest::afterConnect(uv_work_t* req, int status)
  58. {
  59.     HandleScope scope;
  60.     Baton* baton = static_cast<Baton*>(req->data);
  61.     baton->client->Unref();
  62.     Handle<Value> argv[2];
  63.     if(baton->err) {
  64.         argv[0] = Exception::Error(String::New(baton->errMsg->c_str()));
  65.         argv[1] = Undefined();
  66.     } else {
  67.         argv[0] = Undefined();
  68.         //构造client对象
  69.         Handle<Object> client = ClientTest::constructorTemplate->GetFunction()->NewInstance();
  70.         //对象作为回调函数第二个参数
  71.         argv[1] = client;
  72.     }
  73.     //执行回调返回
  74.     node::MakeCallback(Context::GetCurrent()->Global(), baton->callback, 2, argv);
  75.     //删除对象
  76.     delete baton;
  77. }

  78. Handle<Value> ClientTest::test(const Arguments& args)
  79. {
  80.     HandleScope scope;
  81.     return scope.Close(String::New("Hello World!"));
  82. }
baton.cpp

点击(此处)折叠或打开

  1. #include "baton.h"
  2. using namespace v8;

  3. Baton::Baton(ClientTest*c,Handle<Function> *callback)
  4. {
  5.     this->client = c;
  6.     if (callback!=NULL)
  7.     {
  8.         this->callback = Persistent<Function>::New(*callback);
  9.     }
  10.     else
  11.     {
  12.         this->callback = Persistent<Function>();
  13.     }
  14.     this->err = false;
  15.     this->errMsg = NULL;
  16.     this->hostname = "127.0.0.1";
  17.     this->user = " ";
  18.     this->password = " ";
  19. }
  20. Baton::~Baton()
  21. {
  22.     callback.Dispose();
  23.     if(errMsg)
  24.     {
  25.         delete errMsg;
  26.     }
  27. }
asyn.cpp

点击(此处)折叠或打开

  1. #include "baton.h"
  2. #include "client.h"


  3. extern "C" {
  4.     static void init(Handle<Object> target) {
  5.         ClientTest::Init(target);
  6.     
  7.     }
  8.     NODE_MODULE(asyn,init);
  9. }

js代码:

点击(此处)折叠或打开

  1. var addon = require('./asyn');

  2. var connData = {
  3.     "hostname":'localhost',
  4.     "user":'admin',
  5.     "passwd":'admin123'
  6. };
  7. var conn = new addon.ClientTest();

  8. var res = conn.connection(connData,function(err,connect){
  9.     if (err) {
  10.         console.log(err);
  11.     }else
  12.     {
  13.         
  14.         console.log(connect.test());    
  15.     }
  16. });
  17. console.log('ecec before test');
执行结果

 由执行结果可以看出,执行过程中异步执行并不阻塞后续的任务,所以先打印了exec before test.

上一篇:关于windows下vs2010能够定义一个类的空指针并且能够调用类的成员函数说明
下一篇:vs2010在头文件中实现了函数出现的问题