【问题标题】:Concurrent access to groups of objects并发访问对象组
【发布时间】:2011-10-17 19:41:09
【问题描述】:

我正在尝试开发一个由线程池组成的应用程序,使用工作窃取算法并发执行任务。

这些任务

  • 访问一组预定义的对象;
  • 在实际运行之前,必须“原子地”获得对其访问的所有对象的读/写权限;
  • 完成后(并保证最终完成)释放他们获得的对象。

解决此问题的一种可能方法是让每个线程一次执行一项任务,然后尝试使用预定义的顺序锁定每个对象。如果至少有一个失败,则释放所有锁,然后继续执行另一项任务。

然而,这种方法会增加具有大对象依赖性的任务饿死的可能性,甚至可能导致活锁。

是否有另一种方法可以在最大化并发的同时获取一组锁? (没有全局锁)或者可能以不再需要的方式更改系统? 如果是这样,有什么好的论文吗?

ps:正如 thiton 所回答的,这是“哲学家就餐”问题的一般化版本。我正在寻找非集中式解决方案,特别是在高负载(添加和删除任务)中表现良好的算法。

【问题讨论】:

    标签: concurrency scheduled-tasks scheduling mutual-exclusion


    【解决方案1】:

    订购资源是一种有效的方法。想到的另一个直接方法是引入一个共享仲裁器,该仲裁器持有有关资源可用性的信息。任务将通过仲裁器在单个原子步骤“acquire(r1, r2, ..., rn)”中锁定它们需要的所有资源,并使用“release(r1, r2, ..., rn)”类似地释放它们。

    如果“获取”请求 A 可以满足,仲裁器将确保在 A 释放它们之前没有其他任务可以获取 A 持有的任何资源。

    仲裁者可以使用几种策略来满足传入的请求:

    1. 拒绝无法立即满足的请求 - 任务将不得不重试。这为活锁和饥饿打开了大门。
    2. 将所有传入请求保留在一个队列中,并在头部请求所需的资源可用时以 FIFO 方式为它们提供服务。
    3. 将所有未满足的请求保存在一个列表中(不阻塞其所需资源),并在每次释放某些资源时遍历它们(可能对较旧的请求具有优先级),以找到可以满足的请求。

    【讨论】:

    • 这是一个有效的解决方案。但是,由于只有一个仲裁器,所有任务都必须在仲裁器的锁队列中等待,这会导致争用。此外,验证可能并发任务的线程在单个线程中执行,这将限制最大并发性。
    • 现在,考虑到您非常有效的评论,我可能会设计这样一个仲裁器系统,使仲裁器不会处理线程而是任务。与阻塞线程相比,在等待资源时阻塞任务(一项工作)对吞吐量的影响可能要小得多。只为任务和整理资源
    • (续)仲裁器会逐渐读取任务,为它们分配请求的资源,并将获得所有资源的资源吐出到线程池中进行处理。如果任务需要一些时间来执行,单线程仲裁器可能不会成为瓶颈。
    • 我想这一切都归结为任务大小。正如我所设想的系统,这些任务将非常小,可能在数千个 CPU 周期的数量级。这就是我要求去中心化算法的原因。也许我会同时实现这两种方法,看看哪种效果更好。
    【解决方案2】:

    您的问题称为Dining Philosophers。你应该在这个关键字下找到你需要的任何数量的文学作品:-)。

    【讨论】:

    • 我现在想起来了!最准确地说,它是“广义的哲学家进餐”问题,没有 2 个叉子/哲学家的限制。
    【解决方案3】:

    如果一个任务尝试锁定对象只是在某个时间点失败,则另一个任务可能无法锁定一个对象,因为当时第一个任务拥有它。

    我想我会在最初尝试获取锁时使用全局锁,也可能在最终释放它们时使用全局锁。

    当一个简单的解决方案在实践中证明是不够的时,我会担心最大并发性。

    【讨论】:

    • 理论上,这些任务执行得非常快,线程大部分时间都在试图找出要运行的任务。使用全局锁会使问题变得更糟,因为决策过程将由单个线程而不是分布式进行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多