gpt4 book ai didi

javascript - Web Worker 数量限制

转载 作者:行者123 更新时间:2023-12-02 18:46:09 26 4
gpt4 key购买 nike

问题

我发现浏览器可以生成的 Web Worker 的数量是有限制的。

示例

主要的 HTML/JavaScript

<script type="text/javascript">
$(document).ready(function(){
var workers = new Array();
var worker_index = 0;
for (var i=0; i < 25; i++) {
workers[worker_index] = new Worker('test.worker.js');
workers[worker_index].onmessage = function(event) {
$("#debug").append('worker.onmessage i = ' + event.data + "<br>");
};
workers[worker_index].postMessage(i); // start the worker.

worker_index++;
}
});
</head>
<body>
<div id="debug">
</div>

test.worker.js
self.onmessage = function(event) {
var i = event.data;

self.postMessage(i);
};

使用 Firefox(版本 14.0.1,Windows 7)时,这将在容器中仅生成 20 行输出。

问题

有没有解决的办法?我能想到的唯一两个想法是:

1) 菊花链式连接 web worker,即让每个 web worker 产生下一个

例子:
<script type="text/javascript">
$(document).ready(function(){
createWorker(0);
});

function createWorker(i) {

var worker = new Worker('test.worker.js');
worker.onmessage = function(event) {
var index = event.data;

$("#debug").append('worker.onmessage i = ' + index + "<br>");

if ( index < 25) {
index++;
createWorker(index);
}
};
worker.postMessage(i); // start the worker.
}
</script>
</head>
<body>
<div id="debug"></div>

2)将网络 worker 的数量限制为有限数量并修改我的代码以处理该限制(即,在有限数量的网络 worker 之间共享工作负载) - 类似这样: http://www.smartjava.org/content/html5-easily-parallelize-jobs-using-web-workers-and-threadpool

不幸的是,#1 似乎不起作用(只有有限数量的网络 worker 会在页面加载时产生)。还有其他我应该考虑的解决方案吗?

最佳答案

老问题,让我们重温一下!准备好肾上腺素

我一直在研究使用 Web Workers 来隔离 3rd 方插件,因为 Web Workers 无法访问主机页面。我会用你的方法帮助你,我相信你现在已经解决了,但这是给 Internetz 的。然后我会从我的研究中提供一些相关信息。

免责声明 :在我使用您的代码的示例中,我修改并清理了代码以提供不带 jQuery 的完整源代码,以便您和其他人可以轻松运行它。我还添加了一个计时器,它以毫秒为单位提醒执行代码的时间。

在所有示例中,我们引用以下 genericWorker.js文件。

通用 worker .js

self.onmessage = function(event) {
self.postMessage(event.data);
};

方法一(线性执行)

你的第一种方法几乎有效。它仍然失败的原因是您在完成工作后不会删除任何工作人员。这意味着会发生相同的结果(崩溃),只是速度更慢。您只需添加 worker.terminate(); 即可修复它在创建一个新的工作人员以从内存中删除旧的工作人员之前。请注意,这将导致应用程序运行 较慢,因为每个工作程序必须在下一个运行之前创建、运行和销毁。

线性.html
<!DOCTYPE html>
<html>
<head>
<title>Linear</title>
</head>
<body>
<pre id="debug"></pre>
<script type="text/javascript">
var debug = document.getElementById('debug');
var totalWorkers = 250;
var index = 0;
var start = (new Date).getTime();

function createWorker() {
var worker = new Worker('genericWorker.js');
worker.onmessage = function(event) {
debug.appendChild(document.createTextNode('worker.onmessage i = ' + event.data + '\n'));
worker.terminate();
if (index < totalWorkers) createWorker(index);
else alert((new Date).getTime() - start);
};
worker.postMessage(index++); // start the worker.
}

createWorker();
</script>
</body>
<html>

方法二(线程池)

使用线程池应该会大大提高运行速度。与其使用一些带有复杂术语的库,不如简化它。所有线程池意味着让一定数量的 worker 同时运行。我们实际上只需修改线性示例中的几行代码即可获得多线程示例。下面的代码会找出你有多少核(如果你的浏览器支持),或者默认为 4。我发现这段代码比我的 8 核机器上的原始代码快 6 倍。

线程池.html
<!DOCTYPE html>
<html>
<head>
<title>Thread Pool</title>
</head>
<body>
<pre id="debug"></pre>
<script type="text/javascript">
var debug = document.getElementById('debug');
var maxWorkers = navigator.hardwareConcurrency || 4;
var totalWorkers = 250;
var index = 0;
var start = (new Date).getTime();

function createWorker() {
var worker = new Worker('genericWorker.js');
worker.onmessage = function(event) {
debug.appendChild(document.createTextNode('worker.onmessage i = ' + event.data + '\n'));
worker.terminate();
if (index < totalWorkers) createWorker();
else if(--maxWorkers === 0) alert((new Date).getTime() - start);
};
worker.postMessage(index++); // start the worker.
}

for(var i = 0; i < maxWorkers; i++) createWorker();
</script>
</body>
<html>

其他方法

方法三(单worker,重复任务)

在您的示例中,您一遍又一遍地使用同一个工作人员。我知道您正在简化一个可能更复杂的用例,但有些人会看到这一点,并在他们可能只使用一名 worker 完成所有任务时应用此方法。

本质上,我们将实例化一个工作线程,发送数据,等待数据,然后重复发送/等待步骤,直到处理完所有数据。

在我的计算机上,它的运行速度大约是线程池速度的两倍。这实际上让我感到惊讶。我认为线程池的开销会导致它比速度慢 1/2。

重复 worker .html
<!DOCTYPE html>
<html>
<head>
<title>Repeated Worker</title>
</head>
<body>
<pre id="debug"></pre>
<script type="text/javascript">
var debug = document.getElementById('debug');
var totalWorkers = 250;
var index = 0;
var start = (new Date).getTime();
var worker = new Worker('genericWorker.js');

function runWorker() {
worker.onmessage = function(event) {
debug.appendChild(document.createTextNode('worker.onmessage i = ' + event.data + '\n'));
if (index < totalWorkers) runWorker();
else {
alert((new Date).getTime() - start);
worker.terminate();
}
};
worker.postMessage(index++); // start the worker.
}

runWorker();
</script>
</body>
<html>

方法 4(具有线程池的重复工作线程)

现在,如果我们将前面的方法与线程池方法结合起来会怎样?从理论上讲,它应该比以前运行得更快。有趣的是,它的运行速度与我机器上的前一个速度几乎相同。

也许这是每次调用时发送工作人员引用的额外开销。也许是在执行过程中终止了额外的工作人员(只有一名工作人员在我们得到时间之前不会被终止)。谁知道。发现这一点是另一个时间的工作。

重复线程池.html
<!DOCTYPE html>
<html>
<head>
<title>Repeated Thread Pool</title>
</head>
<body>
<pre id="debug"></pre>
<script type="text/javascript">
var debug = document.getElementById('debug');
var maxWorkers = navigator.hardwareConcurrency || 4;
var totalWorkers = 250;
var index = 0;
var start = (new Date).getTime();

function runWorker(worker) {
worker.onmessage = function(event) {
debug.appendChild(document.createTextNode('worker.onmessage i = ' + event.data + '\n'));
if (index < totalWorkers) runWorker(worker);
else {
if(--maxWorkers === 0) alert((new Date).getTime() - start);
worker.terminate();
}
};
worker.postMessage(index++); // start the worker.
}

for(var i = 0; i < maxWorkers; i++) runWorker(new Worker('genericWorker.js'));
</script>
</body>
<html>

现在是一些现实世界的东西

还记得我说过我使用 worker 在我的代码中实现第 3 方插件吗?这些插件有一个状态需要跟踪。我可以启动插件并希望它们不会加载太多导致应用程序崩溃,或者我可以在我的主线程中跟踪插件状态并在插件需要重新加载时将该状态发送回插件。我更喜欢第二个。

我已经写了几个有状态、无状态和状态恢复 worker 的例子,但我不会让你痛苦,只是做一些简短的解释和一些更短的片段。

首先,一个简单的有状态 worker 看起来像这样:

StatefulWorker.js
var i = 0;

self.onmessage = function(e) {
switch(e.data) {
case 'increment':
self.postMessage(++i);
break;
case 'decrement':
self.postMessage(--i);
break;
}
};

它根据收到的消息执行一些操作并在内部保存数据。这很棒。它允许 mah 插件开发人员完全控制他们的插件。主应用程序实例化他们的插件一次,然后会发送消息让他们做一些 Action 。

当我们想一次加载多个插件时,问题就出现了。我们做不到,那我们能做什么?

让我们考虑几个解决方案。

解决方案 1(无状态)

让我们使这些插件无状态。本质上,每次我们想让插件做一些事情时,我们的应用程序应该实例化插件,然后根据它的旧状态向它发送数据。

发送的数据
{
action: 'increment',
value: 7
}

无状态 worker .js
self.onmessage = function(e) {
switch(e.data.action) {
case 'increment':
e.data.value++;
break;
case 'decrement':
e.data.value--;
break;
}
self.postMessage({
value: e.data.value,
i: e.data.i
});
};

这可以工作,但如果我们处理大量数据,这将开始看起来不太完美。另一个类似的解决方案可能是为每个插件配备几个较小的工作人员,并且只向每个插件发送少量数据,但我也对此感到不安。

解决方案2(状态恢复)

如果我们尝试尽可能长时间地将 worker 保留在内存中,但如果我们确实丢失了它,我们可以恢复它的状态怎么办?我们可以使用某种调度程序来查看用户一直在使用哪些插件(可能还有一些花哨的算法来猜测用户将来会使用什么)并将它们保存在内存中。

关于这一点很酷的部分是我们不再考虑每个核心一个 worker 。由于worker活跃的大部分时间都是空闲的,我们只需要担心它占用的内存。对于大量 worker (10 到 20 人左右)来说,这根本不会是实质性的。我们可以保持主要插件加载,而那些不经常使用的插件会根据需要切换出来。所有插件仍然需要某种状态恢复。

让我们使用以下工作程序并假设我们发送“增量”、“减量”或包含它应该处于的状态的整数。

StateRestoreWorker.js
var i = 0;

self.onmessage = function(e) {
switch(e.data) {
case 'increment':
self.postMessage(++i);
break;
case 'decrement':
self.postMessage(--i);
break;
default:
i = e.data;
}
};

这些都是非常简单的例子,但我希望我能帮助理解有效使用多个 worker 的方法!我很可能会为这些东西编写一个调度程序和优化程序,但谁知道我什么时候会达到这一点。

祝你好运,编码愉快!

关于javascript - Web Worker 数量限制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13574158/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com