前言
为了解决我的问题,你必须具备以下方面的知识:线程安全,承诺,异步等待。
对于不熟悉TypeScript的人来说,它只是普通的带有类型注释的JavaScript(ES6)。
我有一个名为excludeItems
的函数,它接受一个项目列表(每个项目都是一个字符串),并为每个项目调用一个API(不包括该项目)。重要的是不要为同一个项目调用API两次,即使在函数的不同执行中也是如此,因此我在本地数据库中保存已被排除的项目。
async function excludeItems(items: string[]) {
var excludedItems = await db.getExcludedItems();
for (var i in items) {
var item = items[i];
var isAlreadyExcluded = excludedItems.find(excludedItem => excludedItem == item);
if (isAlreadyExcluded) {
continue;
}
await someApi.excludeItem(item);
await db.addExcludedItem(item);
}
}
该函数由客户端异步调用几次,这意味着客户端在第一次执行完成之前调用该函数5次。
具体方案:
excludeItems([]);
excludeItems(['A','B','C']);
excludeItems([]);
excludeItems(['A','C']);
excludeItems([]);
在这种情况下,虽然Node.js是单线程的,但临界区问题存在于此处,我得到了错误的结果。这是我的" excludedItems"执行该方案后,在我的本地数据库中收集:
[{item: 'A'},
{item: 'B'},
{item: 'C'},
{item: 'A'},
{item: 'C'}]
正如您所看到的,最后一个' A'和' C'是冗余的(意味着API也被称为这些项目的两次)。
由于代码中的await
语句而发生。每次达到await语句时,都会创建一个新的Promise,因此虽然Node.js是单线程的,但是等待执行的下一个异步函数正在执行,这样这个关键部分就会被并行执行。
为了解决这个问题,我实施了一个锁定机制:
var excludedItemsLocker = false;
async function safeExcludeItems(items: string[]) {
while (excludedItemsLocker) {
await sleep(100);
}
try {
excludedItemsLocker = true;
var excludedItems: string[] = await db.getExcludedItems();
for (var i in items) {
var item = items[i];
var isAlreadyExcluded = excludedItems.find(excludedItem => excludedItem == item);
if (isAlreadyExcluded) {
continue;
}
await someApi.excludeItem(item);
await db.addExcludedItem(item);
}
}
finally {
excludedItemsLocker = false;
}
}
async function sleep(duration: number): Promise<Object> {
return new Promise(function (resolve) {
setTimeout(resolve, duration);
});
}
但是,此实现由于某种原因不起作用。我仍然得到不止一个(涉嫌)&#34;线程&#34;在关键部分,这意味着它仍然会并行执行,而我的本地数据库也会出现相同的错误结果。 BTW sleep
方法按预期工作,其目的只是为下一个等待执行的函数调用提供CPU时间。
有人看到我的实施中发现了什么?
BTW我知道我可以在不实现Lock的情况下实现相同的目标,例如通过在循环内调用db.getExcludedItems
,但我想知道为什么我的Lock实现被破坏了。
答案 0 :(得分:0)
如果参数是:
['A','B','C']
和db.getExcludedItems()返回:
[{item: 'A'},
{item: 'B'},
{item: 'C'}]
然后你试图在一个对象数组中找到一个字符串,它总是返回undefined:
var isAlreadyExcluded = excludedItems.find(excludedItem => excludedItem == item);
只是一个想法,因为我看不到锁定本身的任何问题,它应该按预期工作。