面试官:假如有几十个请求,如何去控制并发?

简介: 面试官:假如有几十个请求,如何去控制并发?

在编程的路上,我一直都是“代码就是生命”的信徒。

最近,我的面试之路有点坎坷,尤其是遇到面试官抛出的一个经典难题:“假如有几十个请求,如何去控制并发?

这道题不仅让我的脑子里立刻浮现出了一场激烈的并发战争,还让我意识到,掌握并发控制技巧是多么重要。

今天,我将分享一下我是如何一步步解决这个问题的。

什么是并发

并发(Concurrency)是指在同一时间段内执行多个任务的能力。

在单核处理器上,并发任务会快速切换执行,从而使得多个任务看起来同时运行;而在多核处理器上,并发任务则可以真正地同时执行。

为什么需要控制并发

虽然并发可以提高程序的效率和响应速度,但如果不加以控制,很容易造成资源竞争、死锁、系统过载等问题。

因此,在处理大量并发请求时,必须合理地控制并发数量,以保证系统的稳定性和性能。

常见的并发控制方法

常见的并发控制方法包括:

  1. 1. 限流(Rate Limiting):限制在一定时间内允许通过的请求数量。
  2. 2. 信号量(Semaphore:使用信号量来控制并发执行的数量。
  3. 3. 队列(Queue):使用队列将请求按顺序排队处理。
  4. 4. 线程池(Thread Pool):使用线程池来限制并发执行的线程数量。

使用 JavaScript 实现并发控制

Promise.all 示例

在 JavaScript 中,可以使用 Promise.all 来处理多个并发请求。以下是一个简单的示例:

const fetch = require('node-fetch');
const urls = [
  'https://jsonplaceholder.typicode.com/posts/1',
  'https://jsonplaceholder.typicode.com/posts/2',
  'https://jsonplaceholder.typicode.com/posts/3',
  // ... 更多 URL
];
function limitConcurrency(urls, limit) {
  const results = [];
  const executing = [];
  
  async function enqueue() {
    if (urls.length === 0) return Promise.resolve();
    
    const url = urls.shift();
    const promise = fetch(url).then(response => response.json());
    results.push(promise);
    
    const e = promise.then(() => executing.splice(executing.indexOf(e), 1));
    executing.push(e);
    
    let r = Promise.resolve();
    if (executing.length >= limit) {
      r = Promise.race(executing);
    }
    
    await r;
    return enqueue();
  }
  return enqueue().then(() => Promise.all(results));
}
limitConcurrency(urls, 2)
  .then(results => {
    console.log('所有请求完成:', results);
  })
  .catch(error => {
    console.error('请求失败:', error);
  });

并发限制示例

使用 Promise.all 时,如果请求数量非常多,可能会导致资源耗尽问题。可以通过自定义并发控制函数来限制并发请求的数量。以下是一个示例:

const fetch = require('node-fetch');
const urls = [
  'https://jsonplaceholder.typicode.com/posts/1',
  'https://jsonplaceholder.typicode.com/posts/2',
  'https://jsonplaceholder.typicode.com/posts/3',
  // ... 更多 URL
];
function limitConcurrency(urls, limit) {
  const results = [];
  const executing = [];
  
  async function enqueue() {
    if (urls.length === 0) return Promise.resolve();
    
    const url = urls.shift();
    const promise = fetch(url).then(response => response.json());
    results.push(promise);
    
    const e = promise.then(() => executing.splice(executing.indexOf(e), 1));
    executing.push(e);
    
    let r = Promise.resolve();
    if (executing.length >= limit) {
      r = Promise.race(executing);
    }
    
    await r;
    return enqueue();
  }
  return enqueue().then(() => Promise.all(results));
}
limitConcurrency(urls, 2)
  .then(results => {
    console.log('所有请求完成:', results);
  })
  .catch(error => {
    console.error('请求失败:', error);
  });

总结

合理地控制并发不仅可以提高程序的效率,还能保证系统的稳定性和安全性。

希望通过本文的介绍,你能在实际项目中灵活运用这些技巧,解决并发控制的问题。

相关文章
|
6天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
3月前
|
缓存 前端开发 中间件
[go 面试] 前端请求到后端API的中间件流程解析
[go 面试] 前端请求到后端API的中间件流程解析
|
3月前
|
并行计算 数据挖掘 大数据
[go 面试] 并行与并发的区别及应用场景解析
[go 面试] 并行与并发的区别及应用场景解析
|
1月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
23 0
|
3月前
|
Java 程序员 调度
面试准备-并发
面试准备-并发
|
3月前
|
消息中间件 Java 中间件
复盘女朋友面试4个月的并发面试题
该文章主要复盘了关于并发的面试题,包括线程池的使用场景、原理、参数合理化设置,以及ThreadLocal、volatile、synchronized关键字的使用场景和原理,还介绍了juc并发工具包中aqs的原理,强调在面试中要将自己理解的点与面试官讲透。
复盘女朋友面试4个月的并发面试题
|
3月前
|
安全 Go 调度
[go 面试] 深入理解并发控制:掌握锁的精髓
[go 面试] 深入理解并发控制:掌握锁的精髓
|
3月前
|
算法 Go 数据库
[go 面试] 并发与数据一致性:事务的保障
[go 面试] 并发与数据一致性:事务的保障
|
3月前
|
NoSQL Go API
[go 面试] 为并发加锁:保障数据一致性(分布式锁)
[go 面试] 为并发加锁:保障数据一致性(分布式锁)
|
3月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。

相关实验场景

更多