使用资源序列化并发操作

时间:2020-03-04 17:26:58

标签: javascript async-await

我有一个操作清单。每个操作使用一个资源。某些操作使用相同的资源。 例如,op1使用与op3相同的资源。 符号代码:

operations=[op1,op2,op3,op4,op5]
for (let i=0;i<operations.length;i++) {
  perform(operations[i])
}

失败,因为op1和op3使用相同的资源。 因此,另一个方法是:

operations=[op1,op2,op3,op4,op5]
for (let i=0;i<operations.length;i++) {
  await perform(operations[i])
}

不会失败,但是op2不必要等待o1完成,即使它们没有使用相同的资源。

其他方法:

operations=[op1,op2,op3,op4,op5]
for (let i=0;i<operations.length;i++) {
  await operations[i].usedResource.isAvailable;
  perform(operations[i])
}

好的,op1运行,op2运行,op3等待被op1阻塞的资源,...但是op4和op5也无缘无故地等待。

有什么想法吗?

3 个答案:

答案 0 :(得分:1)

您可以执行以下操作。

注意-可以以更优化的方式编写此代码。这只是一个想法。

let operation = (operation, time) => () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, time);
    });
};

let op1 = operation("op1", 10000);

let op2 = operation("op2", 2000);

let op3 = operation("op3", 2000);

let op4 = operation("op4", 1000);

let op5 = operation("op5", 5000);

const operations = [
    {name: "op1", operation: op1, resource: "R1", startedAt: 0},
    {name: "op2", operation: op2, resource: "R2", startedAt: 0},
    {name: "op3", operation: op3, resource: "R1", startedAt: 0},
    {name: "op4", operation: op4, resource: "R4", startedAt: 0},
    {name: "op5", operation: op5, resource: "R5", startedAt: 0}
];

let resources = {
    "R1": {
        isAvailable: true,
        queue: []
    },
    "R2": {
        isAvailable: true,
        queue: []
    },
    "R3": {
        isAvailable: true,
        queue: []
    },
    "R4": {
        isAvailable: true,
        queue: []
    },
    "R5": {
        isAvailable: true,
        queue: []
    },
};

async function operationExecutor(operation) {
    if (operation.startedAt === 0) {
        operation.startedAt = performance.now();
    }
    if (!resources[operation.resource].isAvailable) {
        console.log("Operation", operation.name, "waiting for Resource", operation.resource);
        resources[operation.resource].queue.push(operation);
    } else {
        console.log("Operation Started", operation.name);
        resources[operation.resource].isAvailable = false;
        console.log("Resource locked", operation.resource);
        await operation.operation();
        const t1 = performance.now();
        console.log("Resource released", operation.resource);
        resources[operation.resource].isAvailable = true;
        console.log("Operation Completed", operation.name, `in ${(t1 - operation.startedAt).toFixed(2)} milliseconds`);

        if (Array.isArray(resources[operation.resource].queue) && resources[operation.resource].queue.length > 0) {
            operationExecutor(resources[operation.resource].queue.splice(0, 1)[0]);
        }
    }
}

for (let i = 0; i < operations.length; i++) {
    (operationExecutor)(operations[i]);
}

答案 1 :(得分:1)

鉴于这些操作互不依赖于彼此的结果,您可以为每个资源创建并发队列,然后依次依次执行每个操作:

const opsByResource = new Map()
for (const op in operations) {
    const r = op.usedResource
    if (!opsByResource.has(r)) opsByResource.put(r, [])
    opsByResource.get(r).push(op)
}

return Promise.all(Array.from(opsByResource, async ([res, ops]) => {
    for (const op in ops)
        await perform(op)
    console.log("Done with all operations on", res)
}))

答案 2 :(得分:0)

受您的解决方案启发,我这样做是这样的:

  • 每个资源都将锁(承诺)排在队列中。
  • 在执行功能中,我从队列中弹出第一个锁,然后将我的锁(承诺)推到最后。
  • 然后等待第一个锁,执行操作并解决我的锁

我广泛使用受控承诺的实现,这是带有公开的“ resolveTo”函数并具有指定超时后自动解析选项的常规承诺。我想知道是否有人使用类似的东西,我个人无法想象没有它的编程。

相关问题