2012-06-15 17 views
9

Piszę dodatek GUI dla Node.js (wxWidgets) i chcę uruchomić pętlę GUI w osobnym wątku, ponieważ nie sądzę, że byłoby dobrze scalić to z głównym wątkiem i pętlą zdarzeń Node.Node.js C++ Addon: Threading

Jednak nie jestem pewien, jak utworzyć nowy wątek. Mam to uruchomione z uv_queue_work(). Ale nie stworzy to wyłącznego wątku dla GUI, ale użyje puli wątków Node. To może być zły pomysł, ponieważ pracownik pozostanie w trakcie całego czasu pracy. (Nie jestem tego pewien)

Mogę również użyć wxWidgets 'wxThread, działa również. I znalazłem nową funkcję uv_thread_create w libuv git master. Nie mam pojęcia, jak tego użyć, ponieważ nie ma opisu, a poza tym nie jest jeszcze dostępny w stabilnej wersji Node.js.

Moje pytanie: Jaki jest "standardowy" sposób tworzenia wielowątkowego dodatku Node.js, jeśli taki jest? Przyjrzałem się innym projektom, ale znalazłem tylko krótkie wątki robocze przy użyciu libuv.

+0

Nie znam C++, ale zdaję sobie sprawę, że projekt 'węzeł-włókna' tworzy wątki, może warto byłoby tam zajrzeć (na wypadek gdybyś go przegapił): https: // github.com/laverdet/node-fibers/blob/master/src/fibers.cc – alessioalex

+0

Możesz również rzucić okiem na https://github.com/xk/node-threads-a-gogo/ i zobaczyć, w jaki sposób zostały zaimplementowane to. –

Odpowiedz

9

Odpowiedź jest taka, że ​​zazwyczaj chcesz używać wątków w tle zarządzanych przez Nodejs, przesyłając pracę do kolejki zdarzeń UV, a następnie pozwól nodejs martwić się o tworzenie wątków i zarządzanie nimi.

poniżej przykładowy przykład, którego brakuje w podręczniku 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

Wygląda na to, że ktoś zawinął to w ładne rozszerzenie 'npm install nan' dla natywnych rozszerzeń nodejs - https://github.com/rvagg/nan – Soren

+0

Dzięki dokładnie to, czego szukałem! Działa jak marzenie. – webaba