信号量(Semaphore)是一种并发控制机制,用于限制可同时访问某个资源的线程数。在编程中,信号量是一种计数器,用于控制对资源的访问。它的作用类似于锁,但更为灵活。
信号量基本概念
1、计数器 2、P操作(等待) 3、V操作(释放)
信号量模式的应用
信号量模式在编程中应用广泛,常用于控制对共享资源的访问,例如:
- 限制同时访问某个资源的线程或进程数量。
- 控制并发任务的执行数量。
- 实现生产者-消费者模型。
在 JavaScript 中,我们可以使用 async/await 和 Promise 来实现信号量模式,以控制并发请求的数量。实现代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Semaphore</title>
</head>
<body>
<script>
/**
* 核心工作流程:
* - 增加计数器:队列插入 resolve 回调
* - 检查队列: 如果请求队列中有等待的请求,消费计数器 ,执行resolve 回调
*/
class Semaphore {
constructor(maxConcurrent) {
this.maxConcurrent = maxConcurrent;
this.currentCount = 0;
this.queue = [];
}
async acquire() {
if (this.currentCount < this.maxConcurrent) {
this.currentCount++;
return;
}
// 如果当前并发数达到最大值,则创建一个新的 Promise,并将其 `resolve` 方法放入队列,那么这个 Promise 就会一直处于pending状态,即新请求进入等待状态
return new Promise(resolve => this.queue.push(resolve));
}
release() {
if (this.queue.length > 0) {
// 检查队列: 如果队列中有等待的请求,取出队列中的第一个回调函数,即 上面存入的 `resolve` 方法,并执行。那么 Promise 得以被解决,从而解除阻塞状态
const next = this.queue.shift();
next();
} else {
this.currentCount--;
}
}
}
const test = new Semaphore(5);
const makeRequest = async url => {
await test.acquire();
try {
const response = await fetch(url);
const data = await response.json();
return data;
} finally {
test.release();
}
};
const runRequest = async urls => {
const results = await Promise.all(urls.map(url => makeRequest(url)));
console.log('results: ' + results);
};
const urls = [
'https://api.example.com/data1',
'https://api.example.com/data2',
'https://api.example.com/data3',
'https://api.example.com/data4',
'https://api.example.com/data5',
'https://api.example.com/data6',
'https://api.example.com/data7',
];
runRequest(urls);
</script>
</body>
</html>