2012-06-15 16 views
9

Sto scrivendo un addon della GUI per Node.js (wxWidgets) e voglio eseguire il ciclo della GUI in una propria discussione poiché non penso che sarebbe una buona idea unire con il thread principale del nodo e il ciclo degli eventi.Node.js C++ Addon: Threading

Tuttavia, non sono sicuro di come creare un nuovo thread. L'ho fatto funzionare con uv_queue_work(). Ma non creerà un thread esclusivo per la GUI ma userà il pool di thread del Node. E questa potrebbe essere una cattiva idea poiché il lavoratore rimarrà durante l'intero runtime. (Non ne sono sicuro)

Potrei anche usare lo wxThread di wxWidgets, funziona anche. E ho trovato una nuova funzione uv_thread_create in libuv git master. Non ho idea di come usarlo in quanto non esiste una descrizione e inoltre non è ancora disponibile nella build stabile di Node.js.

La mia domanda: qual è il modo "standard" per creare un Addon Node.js multi-thread, se presente? Ho esaminato altri progetti, ma sono riuscito a trovare solo thread di lavoro a esecuzione ridotta usando libuv.

+0

Non so C++ ma sono consapevole del fatto che il progetto 'nodo fibers' crea discussioni, forse sarebbe utile guardare lì (nel caso in cui hai perso esso): https: // github.com/laverdet/node-fibers/blob/master/src/fibers.cc – alessioalex

+0

Puoi anche dare un'occhiata a https://github.com/xk/node-threads-a-gogo/ e vedere come sono implementati esso. –

risposta

9

La risposta è che in genere si desidera utilizzare i thread in background gestiti da Nodejs inviando il proprio lavoro alla coda degli eventi uv, quindi lasciare che nodejs si preoccupi di come creare e gestire i thread.

di seguito è riportato l'esempio di codice che manca nel manuale 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

Sembra che qualcuno abbia completato questo in una bella estensione 'npm install nan' per le estensioni native nodejs - https://github.com/rvagg/nan – Soren

+0

Grazie esattamente a quello che stavo cercando! Funziona come un fascino. – webaba