尝试在TypeScript中实现一个简单的资源锁定系统-包含测试用例

wfauudbj  于 2022-11-26  发布在  TypeScript
关注(0)|答案(1)|浏览(100)

我有一个服务器,它同时接收请求,但是一些R/W资源不能同时访问,而且在数据库级别实现锁对我来说是不现实的,所以我想创建一个函数,将并行请求转换为顺序请求。
这个想法是让队列中的每个都有一个单独的资源id,当收到请求时,如果对应于资源id的队列为空,它就执行我想要的函数,如果不是,它就把要执行的函数排队,在其他函数完成之后执行。
我想出了一些代码是不工作的,我不明白为什么。
下面是小提琴的例子:

export const sleep = (ms: number): Promise<void> =>
  new Promise((resolve) => { setTimeout(resolve, ms); });

type AsyncFn = () => Promise<void>;

const locks = new Map<string, AsyncFn[]>();

const unstackLock = async (id: string) => {
  const stack = locks.get(id);

  if (!stack) {
    return;
  }

  const nextFn = stack.shift();

  if (!nextFn) {
    return;
  }

  try {
    await nextFn();
  } finally {
    await unstackLock(id);
  }
};

export const withLock = async (id: string, fn: AsyncFn): Promise<void> => {
  if (!locks.has(id)) {
    locks.set(id, []);
  }

  const lock = locks.get(id);

  if (!lock) {
    // never happens but makes TS happy
    throw new Error('Lock is not defined');
  }

  lock.push(fn);

  if (lock.length === 1) {
    await unstackLock(id);
  }
};

const test = async () => {
  const results: number[] = [];
    
  const f1 = withLock('lock', async () => {
    await sleep(Math.random() * 100);
    results.push(1);
  });

  const f2 = withLock('lock', async () => {
    await sleep(Math.random() * 100);
    results.push(2);
  });

  const f3 = withLock('lock', async () => {
    await sleep(Math.random() * 100);
    results.push(3);
  });

  await Promise.all([f1, f2, f3]);

  if (results[0] !== 1 || results[1] !== 2 || results[2] !== 3) {
    console.log('FAILED', results);
  } else {
    console.log('SUCCESS');
  }
};

test();

我希望results数组是[1, 2, 3],无论f1f2f3(它们分别将123推入results数组)执行多长时间,但顺序是随机的。

q3qa4bjr

q3qa4bjr1#

好吧,我想到了一个可行的版本:

export const sleep = (ms: number): Promise<void> =>
  new Promise((resolve) => { setTimeout(resolve, ms); });

type AsyncFn = () => Promise<void>;

type Lock = {
  running: Promise<void> | null;
  queue: AsyncFn[];
}

const locks = new Map<string, Lock>();

const unstackLock = async (id: string) => {
  const stack = locks.get(id);

  if (!stack) {
    return;
  }

  if (stack.queue.length === 0) {
    return;
  }

  if (!stack.running) {
    const fn = stack.queue.shift();
    if (fn) {
      stack.running = fn();
      await stack.running;
      stack.running = null;
      await unstackLock(id);
    }
  }
};

export const withLock = async (id: string, fn: AsyncFn): Promise<void> => {
  if (!locks.has(id)) {
    locks.set(id, { running: null, queue: [] });
  }

  const lock = locks.get(id);

  if (!lock) {
    // never happens but makes TS happy
    throw new Error('Lock is not defined');
  }

  lock.queue.push(fn);

  return unstackLock(id);
};

const test = async () => {
  const results: number[] = [];
    
    const f1 = withLock('lock', async () => {
    await sleep(Math.random() * 100);
    results.push(1);
  });

  const f2 = withLock('lock', async () => {
    await sleep(Math.random() * 100);
    results.push(2);
  });

  const f3 = withLock('lock', async () => {
    await sleep(Math.random() * 100);
    results.push(3);
  });

  await Promise.all([f1, f2, f3]);

  if (results[0] !== 1 || results[1] !== 2 || results[2] !== 3) {
    console.log('FAILED', results);
  } else {
    console.log('SUCCESS');
  }
};

test();

相关问题