ajax并行并行执行
HTML5附带的最酷的新功能之一是Web Workers API的Worker
接口。 在此之前,我们不得不介绍一些技巧,以便仍然向用户展示响应式网站。 Worker
接口允许我们创建具有较长运行时间并需要大量计算工作的功能。 此外,可以同时使用Worker
实例,这使我们可以根据需要生成任意数量的Worker
。
在本文中,我将讨论为什么多线程很重要以及如何使用ParallelJS在JavaScript中实现它。
为什么要多线程?
这是一个有效的问题。 从历史上看,产生线程的能力提供了一种优雅的方法来在一个进程中划分工作。 操作系统负责安排为每个线程指定的时间,因此优先级较高且工作更多的线程比低优先级的空闲线程优先。
在过去的几年中,同时多线程(SMT)对于访问现代CPU的计算能力已变得至关重要。 原因很简单:关于每个面积的晶体管数量,摩尔定律仍然有效。 但是,由于多种原因,必须停止频率缩放。 因此,否则必须使用可用的晶体管。 可以确定,体系结构改进(例如SIMD)和多核是最佳选择。
为了使用SMT,我们需要编写并行代码,即为了获得单个结果而并行运行的代码。 我们通常需要考虑特殊的算法,因为大多数顺序代码要么很难并行化,要么效率很低。 原因在于阿姆达尔定律,该定律指出加速比S由
其中N是并行工作程序(例如,处理器,内核或线程)的数量, P是并行分数。 将来,可能会使用许多更依赖于并行算法的核心体系结构。 在高性能计算GPU系统和特殊体系结构领域,例如Intel Xeon Phi,就是这样的平台。
最后,我们应该区分一般的并发应用程序或算法与并行执行。 并行是(可能相关的)计算的同时执行。 相反,并发是独立执行进程的组成。
JavaScript中的多线程
在JavaScript中,我们已经知道如何编写并发程序,即通过使用回调。 现在,也可以将这些知识转移到创建并行程序中!
通过自身的构造,JavaScript在事件循环(通常遵循React器模式)所介导的单个线程中执行。 例如,这为我们提供了一个很好的抽象,用于处理对(外部)资源的异步请求。 它还保证始终在同一执行线程内触发先前定义的回调。 没有跨线程异常,竞争条件或与线程相关的其他问题。 但是,这并不能使我们更接近JavaScript中的SMT。
通过引入Worker
接口,已经找到了解决该问题的理想解决方案。 从我们的主应用程序的角度来看,Web Worker中的代码应被视为并发运行的任务。 通信也以这种方式执行。 我们使用message API,该API也可用于从包含的网站到托管页面的通信。
例如,以下代码通过向发起方发送消息来响应传入的消息。
window.addEventListener('message', function (event) {
event.source.postMessage('Howdy Cowboy!', event.origin);
}, false);
从理论上讲,网络工作者可能还会产生另一个网络工作者。 但是,实际上大多数浏览器都禁止这样做。 因此,Web Worker之间通信的唯一方法是在主应用程序上。 通过消息进行的通信是同时执行的,因此仅存在异步(非阻塞)通信。 起初,这对于编程来说可能很奇怪,但是却带来了许多优点。 最重要的是,我们的代码应该没有竞争条件!
让我们看一个简单的示例,该示例使用两个参数分别表示序列的开始和结束在后台计算素数序列。 首先,我们创建一个名为prime.js
的文件,其内容如下:
onmessage = function (event) {
var arguments = JSON.parse(event.data);
run(arguments.start, arguments.end);
};
function run (start, end) {
var n = start;
while (n < end) {
var k = Math.sqrt(n);
var found = false;
for (var i = 2; !found && i <= k; ++i) {
found = n % i === 0;
}
if (!found) {
postMessage(n.toString());
}
n++;
}
}
现在,我们在主应用程序中仅需要以下代码即可启动后台工作程序。
if (typeof Worker !== 'undefined') {
var w = new Worker('prime.js');
w.onmessage = function(event) {
console.log(event);
};
var args = { start : 100, end : 10000 };
w.postMessage(JSON.stringify(args));
}
很多工作。 特别烦人的是另一个文件的使用。 这产生了很好的分离,但是对于较小的任务似乎是完全多余的。 幸运的是,有一条出路。 考虑以下代码:
var fs = (function () {
/* code for the worker */
}).toString();
var blob = new Blob(
[fs.substr(13, fs.length - 14)],
{ type: 'text/javascript' }
);
var url = window.URL.createObjectURL(blob);
var worker = new Worker(url);
// Now setup communication and rest as before
当然,我们可能希望有一个比这类魔术数字(13和14)更好的解决方案,并且取决于浏览器,必须使用Blob
和createObjectURL
的备用。 如果您不是JavaScript专家, fs.substr(13, fs.length - 14)
作用是提取函数体。 为此,我们将函数声明转换为字符串(使用toString()
调用),然后删除函数本身的签名。
图书馆不能在这里帮助我们吗?
认识ParallelJS
![](https://img-blog.csdnimg.cn/2022010700133966862.png)
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
这就是ParallelJS发挥作用的地方。 它提供了一个不错的API,为Web工作者提供了一些便利。 它包括许多助手和非常有用的抽象。 我们首先提供一些要处理的数据。
var p = new Parallel([1, 2, 3, 4, 5]);
console.log(p.data);
数据字段产生提供的数组。 尚未调用“并行”。 但是,实例p
包含一组方法,例如spawn
,它将创建一个新的Web worker。 它返回一个Promise
,使处理结果变得轻而易举。
p.spawn(function (data) {
return data.map(function (number) {
return number * number;
});
}).then(function (data) {
console.log(data);
});
上面的代码的问题是计算将不会真正并行。 我们只创建一个后台处理程序,一次扫描即可处理整个数据数组。 只有在处理完整个数组后,我们才能获得结果。
更好的解决方案是使用Parallel
实例的map
函数。
p.map(function (number) {
return number * number;
}).then(function (data) {
console.log(data);
});
在前面的示例中,核心非常简单,可能过于简单。 在实际示例中,将涉及许多操作和功能。 我们可以使用require
函数包括引入的函数。
function factorial (n) {
return n < 2 ? 1 : n * factorial(n - 1);
}
p.require(factorial)
p.map(function (n) {
return Math.pow(10, n) / factorial(n);
}).reduce(function (data) {
return data[0] + data[1];
}).then(function (data) {
console.log(data);
});
reduce
函数有助于将分散的结果聚合为单个结果。 一旦知道所有子结果,它便提供了方便的抽象方法,用于收集子结果并执行某些操作。
结论
ParallelJS为我们提供了一种优雅的方式来规避使用Web Worker时可能发生的许多问题。 此外,我们获得了一个不错的API,其中包含一些有用的抽象和帮助程序。 将来可以集成进一步的改进。
除了在JavaScript中使用SMT的功能外,我们可能还希望使用向量化功能。 如果支持的话, SIMD.js似乎是一种可行的方法。 在某些将来(希望不太远),使用GPU进行计算可能也是一个有效的选择。 在Node.js中,存在用于CUDA(并行计算体系结构)的包装器,但是执行原始JavaScript代码仍然不可行。
在此之前,ParallelJS是释放多核CPU强大功能来处理长时间运行的计算的最佳选择。
你呢? 您如何使用JavaScript释放现代硬件的力量?
翻译自: https://www.sitepoint.com/parallel-javascript-with-paralleljs/
ajax并行并行执行