• Web Workers在Web上进行多线程处理的重要性
  • 发布于 2个月前
  • 236 热度
    0 评论
  • 秋萧索
  • 0 粉丝 39 篇博客
  •   
多线程是现代软件开发中使用的一种重要技术,用于提高应用程序的性能和响应能力。然而,由于JavaScript的单线程特性,在Web上并不常见。为了克服这个限制,引入了Web Workers作为在Web应用程序中启用此技术的一种方式。在本文中,Sarah Oke Okolo探讨了Web Workers在Web上进行多线程处理的重要性,包括使用它们的限制和考虑因素,以及减轻与Web Workers相关的潜在问题的策略。

Web Workers是现代Web开发的一个强大功能,并在HTML5规范于2009年中引入。它们旨在提供一种在后台执行JavaScript代码的方式,与网页的主执行线程分离,以提高性能和响应性。

主线程是负责渲染UI、执行JavaScript代码和处理用户交互的单个执行上下文。换句话说,JavaScript是“单线程”的。这意味着任何耗时的任务,例如复杂的计算或数据处理,如果在主线程执行,会阻塞主线程并导致UI冻结和无响应。

这就是Web Workers的作用。

Web Workers被实现为解决这个问题的一种方式,它允许耗时的任务在一个被称为“工作线程”的单独线程中执行。这使得JavaScript代码可以在后台执行,而不会阻塞主线程,导致页面变得无响应。

创建JavaScript中的Web Worker并不是一项复杂的任务。以下步骤为将Web Worker集成到您的应用程序中提供了一个起点:
1.创建一个新的JavaScript文件,其中包含您想要在工作线程中运行的代码。该文件不应包含对DOM的任何引用,因为它将无法访问它。

2.在您的主JavaScript文件中,使用Worker构造函数创建一个新的worker对象。这个构造函数接受一个参数,即您在步骤中创建的JavaScript文件的URL。
const worker = new Worker('worker.js');
3.为worker对象添加事件监听器,以处理主线程和worker线程之间发送的消息。onmessage事件处理程序用于处理来自worker线程的消息,而postMessage方法用于向worker线程发送消息。
worker.onmessage = function(event) {
  console.log('Worker said: ' + event.data);
};
worker.postMessage('Hello, worker!');
4.在您的工作线程JavaScript文件中,使用self对象的onmessage属性添加一个事件监听器,以处理从主线程发送的消息。您可以使用event.data属性访问发送的消息中的数据。
// 堆代码 duidaima.com
self.onmessage = function(event) {
  console.log('Main thread said: ' + event.data);
  self.postMessage('Hello, main thread!');
};
现在让我们运行Web应用程序并测试worker。我们应该在控制台上看到打印的消息,指示主线程和worker线程之间发送和接收了消息。

Web Workers和主线程之间的一个关键区别是,Web Workers无法访问DOM或UI。这意味着它们无法直接操作页面上的HTML元素或与用户交互。另一个重要的区别是,Web Workers被设计为在与主线程分离的沙箱环境中运行,这意味着它们对系统资源的访问受到限制,并且无法访问某些API,例如localStorage或sessionStorage。然而,它们可以通过消息传递系统与主线程进行通信,允许在两个线程之间交换数据。

一.重要性和好处
Web Workers为Web开发人员提供了一种在Web上实现多线程的方式,这对于构建高性能的Web应用程序至关重要。通过将耗时的任务在后台执行,与主线程分离,Web Workers改善了Web页面的整体响应性,并实现了更流畅的用户体验。以下是Web Workers在Web上进行多线程处理的一些重要性和好处。

1.改进的资源利用
通过允许在后台执行耗时任务,Web Workers更有效地利用系统资源,实现更快速和高效的数据处理,提高整体性能。这对于涉及大量数据处理或图像处理的Web应用程序尤为重要,因为Web Workers可以在不影响用户界面的情况下执行这些任务。

2.增加的稳定性和可靠性
通过将耗时的任务隔离到独立的工作线程中,Web Workers有助于防止在主线程上执行大量代码时发生崩溃和错误。这使得开发人员能够编写稳定可靠的Web应用程序,减少用户的不满或数据丢失的可能性。

3.增强的安全性
Web Workers在与主线程分离的沙盒环境中运行,有助于增强Web应用程序的安全性。这种隔离可以防止恶意代码访问或修改主线程或其他Web Workers中的数据,降低数据泄露或其他安全漏洞的风险。

4.更好的资源利用
Web Workers可以通过释放主线程来处理用户输入和其他任务,而Web Workers在后台处理耗时的计算,从而提高资源利用率。这可以帮助改善整体系统性能,减少崩溃或错误的可能性。此外,通过利用多个CPU核心,Web Workers可以更有效地利用系统资源,实现更快、更高效的数据处理。

二.Web Workers 的实际应用
让我们来探讨一些最常见和有用的 Web Workers 应用。无论您是构建复杂的 Web 应用还是简单的网站,了解如何利用 Web Workers 可以帮助您提高性能,提供更好的用户体验。

1.负载均衡 CPU 密集型任务
假设我们有一个需要执行大量 CPU 密集型计算的网络应用程序。如果我们在主线程中执行这个计算,用户界面将变得不响应,用户体验将受到影响。为了避免这种情况,我们可以使用 Web Worker 在后台执行计算。
// Create a new Web Worker.
const worker = new Worker('worker.js');

// Define a function to handle messages from the worker.
worker.onmessage = function(event) {
  const result = event.data;
  console.log(result);
};

// Send a message to the worker to start the computation.
worker.postMessage({ num: 1000000 });

// In worker.js:

// Define a function to perform the computation.
function compute(num) {
  let sum = 0;
  for (let i = 0; i < num; i++) {
    sum += i;
  }
  return sum;
}

// Define a function to handle messages from the main thread.
onmessage = function(event) {
  const num = event.data.num;
  const result = compute(num);
  postMessage(result);
};
在这个例子中,我们创建了一个新的Web Worker,并定义了一个函数来处理来自Worker的消息。然后,我们向Worker发送一条消息,其中包含一个参数(num),用于指定在计算中要执行的迭代次数。Worker接收到这条消息后,在后台执行计算。当计算完成后,Worker向主线程发送一条带有结果的消息。主线程接收到这条消息后,将结果记录到控制台。

这个任务涉及将从0到给定数字的所有数字相加。虽然对于小数字来说,这个任务相对简单和直接,但对于非常大的数字来说,它可能会变得计算密集。

在上面的示例代码中,我们将数字1000000传递给Web Worker中的compute()函数。这意味着compute函数需要将从0加到一百万的所有数字。这涉及大量的额外操作,并且可能需要很长时间才能完成,特别是如果代码在速度较慢的计算机上运行,或者在已经繁忙于其他任务的浏览器标签中运行。

通过将这个任务转移到Web Worker中,应用程序的主线程可以继续平稳运行,而不会被计算密集型任务阻塞。这使得用户界面保持响应,并确保可以处理其他任务,如用户输入或动画,而不会延迟。

2.处理网络请求
让我们考虑一种情况,即Web应用程序需要发起大量的网络请求。在主线程中执行这些请求可能会导致用户界面无响应,从而导致用户体验差。为了解决这个问题,我们可以利用Web Workers在后台处理这些请求。通过这样做,主线程可以保持空闲状态来执行其他任务,而Web Worker可以同时处理网络请求,从而提高性能和改善用户体验。
// Create a new Web Worker.
const worker = new Worker('worker.js');

// Define a function to handle messages from the worker.
worker.onmessage = function(event) {
  const response = event.data;
  console.log(response);
};

// Send a message to the worker to start the requests.
worker.postMessage({ urls: ['https://api.example.com/foo', 'https://api.example.com/bar'] });

// In worker.js:

// Define a function to handle network requests.
function request(url) {
  return fetch(url).then(response => response.json());
}

// Define a function to handle messages from the main thread.
onmessage = async function(event) {
  const urls = event.data.urls;
  const results = await Promise.all(urls.map(request));
  postMessage(results);
};
在这个示例中,我们创建了一个新的Web Worker,并定义了一个函数来处理来自Worker的消息。然后,我们向Worker发送一条消息,其中包含一个要请求的URL数组。Worker接收到这条消息后,使用fetch在后台执行请求。当所有请求完成后,Worker向主线程发送一条带有结果的消息。主线程接收到这条消息后,将结果记录到控制台。

3.并行处理
假设我们有一个需要执行大量独立计算的Web应用程序。如果我们在主线程中按顺序执行这些计算,用户界面将变得不响应,并且用户体验将受到影响。为了避免这种情况,我们可以使用Web Worker在并行中执行这些计算。
// Create a new Web Worker.
const worker = new Worker('worker.js');

// Define a function to handle messages from the worker.
worker.onmessage = function(event) {
  const result = event.data;
  console.log(result);
};

// Send a message to the worker to start the computations.
worker.postMessage({ nums: [1000000, 2000000, 3000000] });

// In worker.js:

// Define a function to perform a single computation.
function compute(num) {
  let sum = 0;
  for (let i = 0; i < num; i++) {
    sum += i;
}
  return sum;
}

// Define a function to handle messages from the main thread.
onmessage = function(event) {
  const nums = event.data.nums;
  const results = nums.map(compute);
  postMessage(results);
};
在这个例子中,我们创建了一个新的 Web Worker,并定义了一个函数来处理来自 Worker 的消息。然后,我们向 Worker 发送一条带有要计算的数字数组的消息。Worker 接收到这条消息后,使用 map 方法并行执行计算。当所有计算完成时,Worker 向主线程发送一条带有结果的消息。主线程接收到这条消息后,将结果记录到控制台中。

三.限制和注意事项
Web workers是提高Web应用程序性能和响应能力的强大工具,但在使用它们时也有一些限制和注意事项需要记住。以下是其中一些最重要的:

1.浏览器支持
Web workers在包括Chrome、Firefox、Safari和Edge在内的所有主流浏览器中都得到支持。然而,仍然有一些其他浏览器不支持Web workers或可能具有有限的支持。

要更详细地了解浏览器的支持情况,请参阅Can I Use。在使用任何功能之前,请务必检查浏览器的支持情况,并对您的应用程序进行全面测试以确保兼容性。

2.有限访问DOM
Web workers在一个单独的线程中运行,无法访问DOM或其他全局对象,如主线程中的windows或documents。为了解决这个限制,您可以使用postMessage方法与主线程通信,并间接更新DOM或访问全局对象。例如,您可以使用将数据发送到主线程,然后根据消息来更新DOM或全局对象。

另外,还有一些库可以帮助解决这个问题。例如,WorkerDOM库允许您在Web worker中运行DOM,从而实现更快的页面渲染和改进的性能。

3.通信开销
Web workers使用postMessage方法与主线程进行通信,这可能会引入通信开销,即在两个或多个计算系统之间建立和维护通信所需的时间和资源量,例如在Web应用程序中的Web Worker与主线程之间的通信。这可能会导致消息处理延迟,潜在地减慢应用程序的运行速度。为了最小化这种开销,您应该只在线程之间传递必要的数据,并避免发送大量数据或频繁的消息。

4.有限的调试工具
与在主线程中调试代码相比,调试Web Workers可能更具挑战性,因为可用的调试工具较少。为了更容易进行调试,您可以使用console API在工作线程中记录消息,并使用browser developer tools检查在线程之间发送的消息。

5.代码复杂度
使用Web Workers可以增加代码的复杂性,因为您需要管理线程之间的通信并确保数据正确传递。这可能会使编写、调试和维护代码更加困难,因此您应仔细考虑是否需要在应用程序中使用Web Workers。

四.缓解其潜在问题的策略
Web Workers是提高Web应用程序性能和响应性的强大工具。但是,在使用Web Workers时,可能会出现几个潜在的问题。以下是一些缓解这些问题的策略:
1.通过消息批处理减少通信开销
消息批处理涉及将多个消息分组为一个批处理消息,这比单独发送消息更高效。这种方法减少了主线程和Web Workers之间的往返次数。它有助于最小化通信开销,提高Web应用程序的整体性能。

要实现消息批处理,您可以使用队列来累积消息,并在队列达到一定阈值或经过一段时间后一起发送。以下是如何在您的 Web Worker 中实现消息批处理的示例:
// Create a message queue to accumulate messages.
const messageQueue = [];

// Create a function to add messages to the queue.
function addToQueue(message) {
  messageQueue.push(message);
  
  // Check if the queue has reached the threshold size.
  if (messageQueue.length >= 10) {
    // If so, send the batched messages to the main thread.
    postMessage(messageQueue);
    
    // Clear the message queue.
    messageQueue.length = 0;
  }
}
// 堆代码 duidaima.com
// Add a message to the queue.
addToQueue({type: 'log', message: 'Hello, world!'});

// Add another message to the queue.
addToQueue({type: 'error', message: 'An error occurred.'});
在这个例子中,我们创建一个消息队列来积累需要发送到主线程的消息。每当使用addToQueue函数将消息添加到队列中时,我们会检查队列是否达到了阈值大小(在这种情况下为十条消息)。如果达到了阈值,我们就使用postMessage方法将批量消息发送到主线程。最后,我们清空消息队列,为下一批消息做准备。

通过以这种方式批量处理消息,我们可以减少主线程和Web Workers之间发送的消息总数。

2.避免使用同步方法
这些是阻塞其他代码执行直到它们完成的JavaScript函数或操作。同步方法可以阻塞主线程,导致应用程序无响应。为了避免这种情况,在您的Web Worker代码中应避免使用同步方法。相反,应使用异步方法,如setTimeout()或setInterval()来执行长时间运算。

这里是一个小演示:
// In the worker
self.addEventListener('message', (event) => {
  if (event.data.action === 'start') {
    // Use a setTimeout to perform some computation asynchronously.
    setTimeout(() => {
      const result = doSomeComputation(event.data.data);

      // Send the result back to the main thread.
      self.postMessage({ action: 'result', data: result });
    }, 0);
  }
});
3.注意内存使用
Web Workers拥有自己的内存空间,其大小取决于用户设备和浏览器设置。为避免内存问题,您应该注意Web Worker代码使用的内存量,并避免不必要地创建大型对象。例如:
// In the worker
self.addEventListener('message', (event) => {
  if (event.data.action === 'start') {
    // Use a for loop to process an array of data.
    const data = event.data.data;
    const result = [];

    for (let i = 0; i < data.length; i++) {
      // Process each item in the array and add the result to the result array.
      const itemResult = processItem(data[i]);
      result.push(itemResult);
    }

    // Send the result back to the main thread.
    self.postMessage({ action: 'result', data: result });
  }
});
在这段代码中,Web Worker处理一个数据数组,并使用postMessage方法将结果返回给主线程。然而,用于处理数据的for循环可能会耗费很多时间。原因是代码一次性处理整个数据数组,这意味着所有数据必须同时加载到内存中。如果数据集非常大,这可能会导致Web Worker消耗大量内存,可能超过浏览器为Web Worker分配的内存限制。

为了解决这个问题,你可以考虑使用内置的JavaScript方法,如forEach或reduce,它们可以逐个处理数据项,避免一次性加载整个数组到内存中。

4.浏览器兼容性
Web Workers 在大多数现代浏览器中都受到支持,但一些旧版浏览器可能不支持它们。为了确保与各种浏览器的兼容性,您应该在不同的浏览器和版本中测试您的 Web Worker 代码。您还可以使用特性检测来在使用代码之前检查是否支持 Web Workers,就像这样:
if (typeof Worker !== 'undefined') {
  // Web Workers are supported.
  const worker = new Worker('worker.js');
} else {
  // Web Workers are not supported.
  console.log('Web Workers are not supported in this browser.');
}
这段代码检查当前浏览器是否支持Web Workers,并在支持的情况下创建一个新的Web Worker。如果不支持Web Workers,则代码会在控制台上记录一条消息,指示浏览器不支持Web Workers。

通过遵循这些策略,您可以确保您的Web Worker代码高效、响应迅速,并与各种浏览器兼容。

结论
随着网络应用程序变得越来越复杂和要求越来越高,高效的多线程技术(如Web Workers)的重要性很可能会增加。Web Workers是现代Web开发的重要特性,它允许开发人员将CPU密集型任务分配到单独的线程中,提高应用程序的性能和响应能力。然而,在使用Web Workers时需要注意一些重要的限制和考虑因素,例如无法访问DOM以及在线程之间传递数据类型的限制。

为了减轻这些潜在问题,开发人员可以采用前面提到的策略,例如使用异步方法并注意任务的复杂性。在未来,使用Web Workers进行多线程处理很可能仍然是提高Web应用程序性能和响应能力的重要技术。虽然JavaScript中还有其他实现多线程的技术,例如使用WebSockets或SharedArrayBuffer,但Web Workers具有几个优势,使其成为开发人员的强大工具。

采用更近期的技术,如WebAssembly,可能会为使用Web Workers分配更复杂和计算密集型的任务开辟新的机会。总体而言,Web Workers有望在未来几年中不断发展和改进,帮助开发人员创建更高效和响应的Web应用程序。

此外,有许多库和工具可用于帮助开发人员使用Web Workers。例如,Comlink和Workerize提供了简化的API,用于与Web Workers进行通信。这些库抽象了管理Web Workers的一些复杂性,使我们更容易利用它们的优势。

希望本文能够让您对Web Workers用于多线程处理的潜力以及如何在自己的代码中使用它们有一个很好的理解。
用户评论