JavaScript 多线程

34
我正在比较几种实现(真实或虚假)JavaScript多线程的不同方法。据我所知,只有Web Workers和Google Gears WorkerPool能提供真正的线程(即在多个处理器上具有真正的并行执行)。我发现了以下方法:
  • 使用yield()在任务之间进行切换
  • 使用setInterval()(或其他非阻塞函数),线程等待彼此
  • 使用Google Gears WorkerPool线程(带插件)
  • 使用HTML5 Web Workers
我阅读了相关问题并找到了上述方法的几个变化,但大多数这些问题都比较久远,可能会有一些新想法。
我想知道——还有什么其他方法可以实现JavaScript多线程?还有其他重要的方法吗?
更新:如评论中指出的那样,我真正的意思是并发。
更新2:我找到了信息,Silverlight+JScript支持多线程,但我无法验证这一点。
更新3:Google已经弃用了Gears:http://code.google.com/apis/gears/api_workerpool.html

4
我想你的问题是如何在Javascript中实现并发。正如你所说,调用yield()或使用setInterval()并不算。而且Web Workers与本地操作系统线程并不完全相同,因为代码必须被隔离在单独的文件中,并且只能通过事件进行通信。此外,workers在底层可能或可能不是使用真正的操作系统线程 -- 尽管它们可以让你轻松实现类似并发的效果。 - Peter
1
没错。我的错,没有使用正确的词汇。 - Chris Hasiński
6
除了插件以外,我认为你的列表很好。我认为Web Worker确实是最好(兼容性最好)的选择,尽管不能保证它们会使用多个CPU核心,这取决于JavaScript引擎。 - Peter
5个回答

29
Web Workers 是一个 W3C 标准(目前是工作草案),无需插件即可使用:
该规范定义了一种 API,允许 Web 应用程序创建后台线程并以并行方式运行脚本,与其主页面相分离。
该规范还讨论了将工作线程分散到多个核心以实现真正并发的方法(由浏览器的 JavaScript 引擎在后台处理):
随着多核 CPU 的普及,一种获得更好性能的方式是将计算密集型任务分配给多个工作线程。例如,对于从 1 到 10,000,000 的每个数字要执行的计算密集型任务,可以将其分配给十个子线程。 yield()setInterval() 只是安排事情稍后发生,它们不会和其他任何东西同时运行。

但是对于这个链接加一,寻找JS并发的人可能会觉得它很有用。 - Chris Hasiński
1
@KrzysztofHasiński 我认为现在 Web Workers 是在 JavaScript 中在浏览器中拥有多个线程的最佳/唯一方法。 - s4y
1
我同意,这是最好的方法,但我并不真正想选择最好的方法,我只想收集不同的方法,并比较在Chrome和Fx中的实现和性能,以便进行简短的讲座。 - Chris Hasiński
2
经过很长一段时间,没有什么新的东西了,我想指出寻找在JS中实现并发的正确方向的人们,所以我接受这个答案 :) - Chris Hasiński
要充分利用Web Workers的能力,你必须使用“可传递对象”,这可以在主线程和工作线程之间移动大块数据而无需复制。通过传递对象的“结构化复制”通常会破坏使用worker带来的性能优势。 - Pawel
似乎多个WebWorker在处理大型任务时并不能提高速度... https://stackoverflow.com/questions/44808354/is-multithreading-with-webworkers-ineffective - Thomas Wagenaar

4
我在想,除了用这种方式,在JavaScript中还有其他方法可以实现多线程吗?还有其他重要的方法吗?
你可以将代码转换成JavaScript代码,其中没有任何显式循环或直接函数调用,而是将代码分成小的执行单元,由线程引擎管理。在我的示例代码中,我展示了一个带有循环的函数如何被转换,但为了保持示例简单,我省略了函数调用的机制。
转换的过程基本上是通过在分割点处分割代码。这些分割点是函数调用和循环(如上所示)。在示例中,我使用了对象和键,但是如果单元堆栈存储为对象变量(即使用this.foo = bar而不是stack["foo"] = bar),它可能更容易在浏览器的JavaScript引擎上运行。
例如以下代码:
// Phoney method purely to demonstrate structure
function Foo() {
  var i,
      sum = 0,
      accumulator_list = [],
      accumulator_modulus = [],
      kMaxAccumulatorCount = 100;

  // Calculate accumulations
  for(i = 0; i < kMaxAccumulatorCount; ++i) {
    current_accumulator = GetNextAccumulator()
    accumulator_list[i] = current_accumulator;
    sum = sum + current_accumulator;
  }

  // Calculate accumulator modulus
  for(i = 0; i < kMaxAccumulatorCount; ++i) {
    current_accumulator = accumulator_list[i];
    accumulator_modulus[i] = current_accumulator % kMaxAccumulatorCount;
  }
}

将文本转换成这样的形式:
function Foo_A(caller,stack) {
  var stack = {};
  stack["i"] = undefined;
  stack["sum"] = 0;
  stack["accumulator_list"] = [];
  stack["accumulator_modulus"] = [];
  stack["kMaxAccumulatorCount"] = 100;

  stack["i"] = 0;
  return {caller: caller, stack: stack, next=Foo_B};
}

function Foo_B(caller, stack) {
  stack["current_accumulator"] = GetNextAccumulator();
  stack["accumulator_list"][stack["i"]] = stack["current_accumulator"];
  stack["sum"] = stack["sum"] + stack["current_accumulator"];

  // For-loop condition satisfied ?
  if(stack["i"] < stack["kMaxAccumulatorCount"]) {
    ++stack["i"];
    return {caller: caller, stack: stack, next:Foo_B};
  } else {
    // Initialise the next for loop.
    stack["i"] = 0;
    return {caller: caller, stack: stack, next:Foo_C};
  }
}

function Foo_C(caller, stack) {
  stack["current_accumulator"] = stack["current_accumulator"][stack["i"]];
  stack["accumulator_modulus"][stack["i"]] = stack["current_accumulator"] % stack["kMaxAccumulatorCount"];

  // For-loop condition satisfied ?
  if(stack["i"] < stack["kMaxAccumulatorCount"]) {
    ++stack["i"];
    return {caller: caller, stack: stack, next:Foo_C};
  } else {
    // Function has finished so the next will be null. When the thread-engine sees this it simulates the behaviour of a return, pops its virtual stack and returns execution to the caller
    return {caller: caller, stack: stack, next:null};
  }
}

3

Multithread.js 是一个库,它能够帮助你在 JS 中实现非常容易的多线程,它包装了 Web Workers 并自动进行大部分工作。 :)


1
不错的补充,尽管它们仍然是Web Workers :) - Chris Hasiński

1

JavaScript中没有直接支持多线程的功能。但是,您可以通过应用一些思想和方法来实现多线程。

其中有一些方法如下:

var id = window.timeout("javascript code", time);

在这里,JavaScript代码在指定的时间后被调用,我们可以使用它

window.clearTimeout(id);

清除缓存。 通过这样我们可以实现假并发。


这只是 setInterval() 的一种变体。而且,正如问题中指出的那样,有一种直接的多线程处理方式(Web Workers)。 - Chris Hasiński
+1 提到了 clearTimeout(),我在我的演示代码中使用了它。 - Chris Hasiński

1

问:除了使用JavaScript,还有哪些方法可以实现并发?

您可以使用异步或“非阻塞”类型的方法。这是Node.js系统的主要亮点之一。虽然它不完全是多线程的,但它确实更快。


1
除了 setIntervalsetTimeout,还有哪些非阻塞方法可以使用?有没有任何与这两种方法显著不同的方法? - Chris Hasiński

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接