sách gpt4 ai đã đi

JavaScript, bất đồng bộ, khóa?

In lại 作者:行者123 更新时间:2023-11-28 17:41:40 28 4
mua khóa gpt4 Nike

我知道异步不是并行的,但我现在遇到了一个非常有趣的情况。

async function magic(){
/* some processing here */
await async () => await prompt_for_user(); // 1st prompt
await async () => await prompt_for_user(); // 2nd prompt
}

magic(); // first
magic(); // second
magic(); // third

从上面的程序中,我们可以轻松预测所有提示会同时弹出。我尝试使用以下实现的队列来解决它:

 const Queue = () => {
let promise;
return async (f) => {
while(promise) await promise;
promise = f();
const res = await promises;
promise = undefined;
return res;
};
};


const queue = Queue();
async function magic(){
/* some processing here */
await queue(async () => await prompt_for_user()); // 1st prompt
await queue(async () => await prompt_for_user()); // 2nd prompt
}

magic(); // first
magic(); // second
magic(); // third

这会阻止一次全部弹出提示。但还有第二个问题:

所以当第一个 magic() 被调用时。向用户显示提示 first.1。程序继续并调用第二个 magic()。另一个提示 second.1 正在等待第一个提示完成后再出现。然后程序继续,调用第三个 magic(),并且 third.1 再次等待 first.1 完成。当 first.1 完成时,意味着用户已经输入了值,second.1 将首先弹出,但我希望 first.2 弹出先起来。

我知道一个明显的解决方案是一一等待魔法。但是这样就会失去js给我们带来的异步优势。如果提示前处理的魔法较多,则提示前需要一些时间。

想法?

1 Câu trả lời

由于在您发布信号量答案之前我无法理解您的总体目标,因此我将尝试回答的问题定义如下。

  1. 您希望以尽可能多的并行度运行一系列异步操作(串行化所需的最少数量)。
  2. 一项或多项操作可能需要提示用户输入信息。
  3. 所有用户提示都必须按照代码的精确顺序排列。因此,第一个被调用和提示的函数必须首先提示。
  4. 如果同一函数多次提示,则其所有提示必须出现在其他提示之前。
  5. 所以基本上,所有提示都必须序列化,但提示之前或之后出现的任何其他异步内容都可以并行运行。
  6. 最好的代码不会“旋转”或“轮询”标志,而是会在等待的内容准备好运行时收到通知,从而为其他操作保留 CPU 周期(promise 背后的一大原则)。

这是一个方案,以您发布的方案为模型,但它使用一系列 promise (不旋转或轮询标志)来强制序列化 prompt() 调用( >.start().end() 调用,同时允许所有其他操作并行运行。这对于 CPU 使用率来说应该会更加高效。

let Semaphore = (function() {
// private data shared among all instances
let sharedPromise = Promise.resolve();

return class Sempaphore {
constructor() {
let priorP = sharedPromise;
let resolver;

// create our promise (to be resolved later)
let newP = new Promise(resolve => {
resolver = resolve;
});

// chain our position onto the sharedPromise to force serialization
// of semaphores based on when the constructor is called
sharedPromise = sharedPromise.then(() => {
return newP;
});

// allow caller to wait on prior promise for its turn in the chain
this.start = function() {
return priorP;
}

// finish our promise to enable next caller in the chain to get notified
this.end = function() {
resolver();
}
}
}
})();

// use random times to test our async better
function prompt(tag, n) {
log(tag, 'input please: ', n);
return new Promise((resolve) => {
setTimeout(resolve, Math.floor(Math.random() * 1000) + 500);
});
};

function log(...args) {
if (!log.start) {
log.start = Date.now();
}
let diff = ((Date.now() - log.start) / 1000).toFixed(3);
console.log(diff + ": ", ...args);
}

function randomDelay(low = 500, high = 1000) {
return new Promise((resolve) => {
setTimeout(resolve, Math.floor(Math.random() * (high - low)) + low);
});
}

async function magic1(tag){
// declare semaphore before any async code to reserve your order for semaphored code below
let s = new Semaphore();

// whatever sync or async code you want here
log(tag, 'do some busy async work 1a');
await randomDelay(800, 1200);
log(tag, 'do some busy work 1b');

// start of our serialized critical section
await s.start();
await prompt(tag, 1);
await prompt(tag, 2);
s.end();
// end of our serialized critical section

// whatever sync or async code you want here
log(tag, 'do more busy work 1c');
await randomDelay();
}

async function magic2(tag){
let s = new Semaphore();
log(tag, 'do some busy async work 2a');
// this delay purposely causes magic2 async delay to be shorter
// than magic1 for testing purposes
await randomDelay(100, 750);
log(tag, 'do some busy work 2b');
await s.start();
await prompt(tag, 3);
await prompt(tag, 4);
s.end();
log(tag, 'do more busy work 2c');
await randomDelay();
}

Promise.all([
magic1("magic1a"),
magic1("magic1b"),
magic2("magic2a"),
magic2("magic2b")
]).then(() => {
log("all done");
}).catch(err => {
log("err: ", err);
});

这是一些示例输出(由于出于测试目的而进行的随机异步延迟,输出会略有不同)。但是,输入调用将始终按照完全相同的顺序:

0.000: magic1a do some busy async work 1a
0.003: magic1b do some busy async work 1a
0.004: magic2a do some busy async work 2a
0.004: magic2b do some busy async work 2a
0.600: magic2b do some busy work 2b
0.721: magic2a do some busy work 2b
0.829: magic1b do some busy work 1b
1.060: magic1a do some busy work 1b
1.061: magic1a input please: 1
2.179: magic1a input please: 2
2.860: magic1a do more busy work 1c
2.862: magic1b input please: 1
3.738: magic1b input please: 2
4.500: magic1b do more busy work 1c
4.502: magic2a input please: 3
5.845: magic2a input please: 4
6.497: magic2a do more busy work 2c
6.498: magic2b input please: 3
7.516: magic2b input please: 4
8.136: magic2b do more busy work 2c
9.097: all done

一些解释:

  1. 在代码中放置 let s = new Sempaphore(); 的位置就是该函数“将自身放入队列”以进行序列化的位置,以便尚未将其自身放入队列中的东西在行中,它的关键部分被迫位于该函数的关键部分之后。这“保留”了一个队列位置,但实际上尚未开始关键部分。如果您有其他不确定的异步代码在关键部分之前运行,这一点很重要。您需要在异步代码之前保留排队位置,但直到关键部分之前才真正等待排队位置。

  2. 在函数中放置 await s.start(); 的位置是您希望它实际等待关键部分的位置的位置。

    <
  3. 放置 s.end() 的位置是关键部分的末尾(允许其他关键部分现在也可以在轮到它们时运行)。

  4. 此演示显示了在提示的关键部分之前和之后运行的异步代码。该代码可以与其他代码并行运行。

  5. 即使在同一关键部分(根据设计),非输入相关的异步操作也可以在输入提示之间交错。仅输入提示被强制序列化。

关于JavaScript、异步、锁?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47797221/

28 4 0
行者123
Hồ sơ cá nhân

Tôi là một lập trình viên xuất sắc, rất giỏi!

Nhận phiếu giảm giá Didi Taxi miễn phí
Mã giảm giá Didi Taxi
Giấy chứng nhận ICP Bắc Kinh số 000000
Hợp tác quảng cáo: 1813099741@qq.com 6ren.com