这两天看了一下v8的在多线程情况下多个Isolate的调度原理,别的不多说了还是先看一下测试代码吧
#include <v8.h> #include <iostream> #include <pthread.h> #include <unistd.h> using namespace v8; typedef struct _Args { Isolate* isolate; char message[256]; } Args; void* test(void* data) { Args* args = (Args*) data; Isolate* isolate = args->isolate; if (isolate == NULL) { std::cout << "null isolate found" << std::endl; delete args; return NULL; } Isolate* current = Isolate::GetCurrent(); if (current == NULL) { std::cout << "current isolate is null before locker" << std::endl; } else { std::cerr << "current isolate is not null before locker" << std::endl; } Locker locker(isolate); current = Isolate::GetCurrent(); if (current != NULL) { std::cerr << "current isolate is not null after locker" << std::endl; } else { std::cout << "current isolate is null after locker" << std::endl; } Isolate::Scope isolate_scope(isolate); current = Isolate::GetCurrent(); if (current == NULL) { std::cerr << "current isolate is null after enter" << std::endl; } else { std::cout << "current isolate is not null after enter" << std::endl; } // Create a stack-allocated handle scope. HandleScope handle_scope(isolate); // Create a new context. Handle<Context> context = Context::New(isolate); // Here's how you could create a Persistent handle to the context, if needed. Persistent<Context> persistent_context(isolate, context); // Enter the created context for compiling and // running the hello world script. Context::Scope context_scope(context); // Create a string containing the JavaScript source code. Handle<String> source = String::New(args->message); // Compile the source code. Handle<Script> script = Script::Compile(source); // Run the script to get the result. Handle<Value> result = script->Run(); // The persistent handle needs to be eventually disposed. persistent_context.Dispose(); // Convert the result to an ASCII string and print it. String::AsciiValue ascii(result); for (int i = 0; i < 3; i++) { std::cout << *ascii << std::endl; sleep(1); } delete args; return NULL; } int main(int argc, char* argv[]) { pthread_t pids[256]; int num = 3; for (int i = 0; i < num; i++) { Args* args = new Args(); args->isolate = Isolate::New(); int n = sprintf(args->message, "\"first thread\" + %d", i); args->message[n] = '\0'; int ret = pthread_create(&pids[i], NULL, test, args); if (ret != 0) { std::cout << "create pthread" << i << " failed" << std::endl; return 1; } } for (int i = 0; i < num; i++) { pthread_join(pids[i], NULL); } std::cerr << "first test loop done" << std::endl; Isolate* isolate = Isolate::New(); { Locker locker(isolate); Isolate::Scope scope(isolate); } for (int i = 0; i < num; i++) { pids[i] = 0; Args* args = new Args(); args->isolate = isolate; int n = sprintf(args->message, "\"second thread\" + %d", i); args->message[n] = '\0'; int ret = pthread_create(&pids[i], NULL, test, args); if (ret != 0) { std::cout << "create pthread" << i << " failed" << std::endl; return 1; } } for (int i = 0; i < num; i++) { pthread_join(pids[i], NULL); } std::cerr << "second test loop done" << std::endl; Isolate* current = Isolate::GetCurrent(); for (int i = 0; i < num; i++) { pids[i] = 0; Args* args = new Args(); args->isolate = current; int n = sprintf(args->message, "\"third thread\" + %d", i); args->message[n] = '\0'; int ret = pthread_create(&pids[i], NULL, test, args); if (ret != 0) { std::cout << "create pthread" << i << " failed" << std::endl; return 1; } } for (int i = 0; i < num; i++) { pthread_join(pids[i], NULL); } std::cerr << "third test loop done" << std::endl; return 0; }
current isolate is null before lockercurrent isolate is null before locker current isolate is null after locker current isolate is not null after enter current isolate is null before locker first thread2 current isolate is null after locker current isolate is not null after enter current isolate is null after locker current isolate is not null after enter first thread1 first thread0 first thread2 first thread1 first thread0 first thread2 first thread1 first thread0 first test loop donecurrent isolate is null before locker current isolate is null before lockercurrent isolate is null after locker current isolate is not null after enter current isolate is null before locker second thread0 second thread0 second thread0 current isolate is null after locker current isolate is not null after enter second thread1 second thread1 second thread1 current isolate is null after locker current isolate is not null after enter second thread2 second thread2 second thread2 second test loop done current isolate is null before locker current isolate is null before locker current isolate is null before locker current isolate is not null after locker current isolate is not null after enter third thread0 third thread0 third thread0 current isolate is not null after enter current isolate is not null after locker third thread1 third thread1 third thread1 current isolate is not null after entercurrent isolate is not null after locker third thread2 third thread2 third thread2 third test loop done
第一组测试是完成多个Isolate对象分别在不同的thread里运行的情况,从结果可以看到,这些Isolate在并行执行,并且当执行了Isolate::Scope之后Isolate::GetCurrent返回值也不空了
第二组测试是完成一个Isolate对象分别在不同的thread里运行的情况,从结果可以看到,这个Isolate实际上是顺序在执行的,同样的,执行了Isolate::Scope之后,Isolate::GetCurrent返回值也不空了
第三组测试是完成DefaultIsolate对象分别在不同的thread里运行的情况,从结果可以看到,DefaultIsoalte实际上也是顺序在执行的,但是与第二组不同的是,Locker执行完成之后Isolate::GetCurrent就已经不空了
其实到了这里,已经很明显了,Locker是用来保证一个Isolate必定同时在一个thread里运行,Isolate::Scope实际上只执行了一句代码Isolate::Enter,它负责将thread local store数据写入,保证后面的执行中依赖于tls的地方都不会出问题,而为什么第三组用DefaultIsolate出现了不同的结果呢
原因其实就在于Locker里对DefaultIsolate做了处理,直接Enter了,看代码
if (isolate_->IsDefaultIsolate()) { // This only enters if not yet entered. internal::Isolate::EnterDefaultIsolate(); }