2012-06-15 6 views
9

Я пишу GUI-аддон для Node.js (wxWidgets), и я хочу запустить цикл GUI в собственном потоке, поскольку я не думаю, что было бы неплохо объединить это с основным потоком Node и циклом событий.Node.js C++ Addon: Threading

Однако я не уверен, как создать новую тему. Я получил его с uv_queue_work(). Но он не будет создавать эксклюзивный поток для графического интерфейса, но использовать пул потоков Node. И это может быть плохой идеей, так как рабочий останется в течение всего времени выполнения. (Не уверен об этом)

Я также могу использовать wxWidgets 'wxThread, работает тоже. И я нашел новую функцию uv_thread_create в libuv git master. Не знаю, как использовать это, поскольку описания нет, и, кроме того, он еще не доступен в стабильной сборке Node.js.

Мой вопрос: что такое «стандартный» способ создания многопоточного Node.js Addon, если таковой имеется? Я посмотрел на другие проекты, но мог найти только короткие рабочие потоки, используя libuv.

+0

Я не знаю, C++, но я знаю, что проект 'узел-fibers' создает потоки, возможно, было бы полезно посмотреть там (в случае, если вы пропустили его): https: // github.com/laverdet/node-fibers/blob/master/src/fibers.cc – alessioalex

+0

Вы также можете посмотреть https://github.com/xk/node-threads-a-gogo/ и посмотреть, как они реализованы Это. –

ответ

9

Ответ заключается в том, что вы обычно хотите использовать потоки фона, управляемые Nodejs, отправив свою работу в очередь событий uv, а затем пусть nodejs беспокоится о том, как создавать и управлять потоками.

ниже - пример шаблона, отсутствующий в руководстве node.js v0.10.

struct Baton 
{ 
    // we need this structure to interact with the uv 
    // the uv_work_t must be the initial element and should store 
    // the callback function to be useful, but the rest 
    // is user defined depending on what is needed to actually do the work. 
    uv_work_t     request; 
    v8::Persistent<v8::Function> callback; 
    // Add more elements to the structure as needed 
    int       countdown; 
}; 


static void AsyncTestWork (uv_work_t* req); 
static void AsyncTestAfter(uv_work_t* req,int status); 
static Handle<Value> AsyncTestPrep(const Arguments& args) 
{ 
    HandleScope scope; 
    if (args.Length() != 1) { 
     ThrowException(Exception::TypeError(String::New("Wrong number of arguments -- needs (callback)"))); 
     return scope.Close(Undefined()); 
    } 

    if (!args[0]->IsFunction()) { 
     ThrowException(Exception::TypeError(String::New("Wrong type of arguments -- needs (callback)"))); 
     return scope.Close(Undefined()); 
    } 

    v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[0]); 

    Baton* baton = new Baton(); 
    baton->request.data = baton; 
    baton->callback = v8::Persistent<v8::Function>::New(callback); 
    baton->countdown = 3; 

    uv_queue_work(uv_default_loop(), &baton->request, AsyncTestWork, AsyncTestAfter); 

    return scope.Close(v8::Undefined()); 
} 


static void AsyncTestWork (uv_work_t* req) 
{ 
    // This method will run in a seperate thread where you can do 
    // your blocking background work. 
    // In this function, you cannot under any circumstances access any V8/node js 
    // valiables -- all data and memory needed, MUSt be in the Baton structure 
    Baton* baton = static_cast<Baton*>(req->data); 
    sleep(6); // some fictional work, delaying the return.... 
    baton->countdown -= 1; // my actual work in this 
} 

static void AsyncTestAfter(uv_work_t* req,int status) 
{ 
    // This is what is called after the 'Work' is done, you can now move any data from 
    // Baton to the V8/Nodejs space and invoke call back functions 

    Baton* baton = static_cast<Baton*>(req->data); 

    v8::Handle<v8::Value> argv1[] = { v8::Null(), Number::New(baton->countdown) }; 
    v8::Handle<v8::Value> argv2[] = { v8::Null(), Number::New(23) }; 

    v8::TryCatch try_catch; 
     // Call back to the JS function, you can make as many/few callbacks 
     // as you need, they just go on the event loop queue for now. 
     // Note: that it is mostly customary to call the callback 
     // function just (exactly) which is probably what you want 
     // to do to avoid confusing your users if you make a public api 
     baton->callback->Call(v8::Context::GetCurrent()->Global(), 2, argv1); 
     baton->callback->Call(v8::Context::GetCurrent()->Global(), 2, argv2); 
    if (try_catch.HasCaught()) { 
     node::FatalException(try_catch); 
    } 

    if (baton->countdown > 0) { 
     // resubmit the worker for yet more work 
     uv_queue_work(uv_default_loop(), &baton->request, AsyncTestWork, AsyncTestAfter); 
    } else { 
     // we are finished, clean up and be done 
     baton->callback.Dispose(); 
     delete baton; 
    } 
} 


void init(Handle<Object> exports) 
{ 

    exports->Set(String::NewSymbol("myAsyncTestFunction"), 
       FunctionTemplate::New(AsyncTestPrep)->GetFunction()); 

} 
+2

Похоже, что кто-то завернул это в приятное расширение 'npm install nan' для собственных расширений nodejs - https://github.com/rvagg/nan – Soren

+0

Спасибо, что именно я искал! Работает как шарм. – webaba