【问题标题】:Locking any of multiple locks锁定多个锁中的任何一个
【发布时间】:2016-06-27 16:17:14
【问题描述】:

我有一个“帐户”池。 每个线程一次只能由一个线程使用。 所以起初我认为一个简单的锁就足够了。 但事实证明这并不容易。

唯一可行的方法是使用Monitor.TryEnter,超时为0,然后永远循环遍历对象,直到某个时候我成功锁定一个帐户,然后使用它。

这在计算机资源上显然不是很容易,而且通常风格不好。

我的下一个想法是给每个对象一个这样的动作队列 List<Action<Account> queue 但不知何故,这似乎也不是很优雅。

我对帐户执行的操作也是async Task,这使得一切变得更加复杂。 (必须使用 Nito.AsyncEx 而不是普通的 lock())

通常,只要执行我的操作,我就不会关心我使用哪个帐户。所以我使用目前可用的第一个帐户。 但有时我什至还想对特定帐户进行操作。

我应该如何设计我的“AccountPool”,以便我可以轻松拥有ExecuteOnFirstUnusedAccount(Action<Account> action)ExecuteOnSpecificAccount(Func<Account,bool> filter, Action<Account> action) 之类的方法?

编辑: 我刚刚想到的一种可能的解决方案如下。

每个帐户都有自己的任务(或线程,没关系)。然后有一个(全局)BlockingCollection<Action<Account>>,然后每个任务调用集合上的.Take()

问题:当我需要让一个特定帐户执行一些不再可能的操作时,因为它们都在等待阻塞集合。

【问题讨论】:

  • 锁的想法有什么问题?如果每个帐户都有自己的锁,并且对帐户进行操作的每个线程都获取该帐户的锁并在完成后释放它,则应该没有问题。请显示更多代码。
  • 将账户入栈,访问时锁定账户栈,想使用时从栈中弹出账户,任务完成后推回。
  • @Great.And.Powerful.Oz 不可能,假设堆栈为空并且必须执行另一个动作。我会保留堆栈上的锁吗?不,那时没有其他线程可以输入帐户了。所以我再次释放锁然后什么,过一段时间重试?我认为基于等待重试的解决方案不是很好:(
  • @Felheart,好的,这与使用所有帐户的列表有何不同?等待/重试是访问有限资源的方式,或者您创建一个新帐户。而且,是的,当一个线程使用一个帐户完成时,如果等待/重试编码正确,它确实可以将其放回原处。这是一个众所周知的问题,并且正是线程池的工作原理。
  • @Great.And.Powerful.Oz 我假设你的意思不是“等待 x 毫秒,然后重试”通过“等待重试”,而是更复杂的东西,比如一些事件帐户再次可用时立即通知/通知?所以我们实际上并没有等待某个固定的时间,而是知道什么时候有一些变化?

标签: c# multithreading asynchronous async-await task


【解决方案1】:

想象一下出租车站。当没有乘客时,出租车会排成一队。当没有出租车时,乘客会形成另一个队列。账户好比出租车,行为好比客户。这样ExecuteOnFirstUnusedAccount就实现了。 ExecuteOnSpecificAccount 需要更多的工作。对于每个特定帐户。为动作创建一个队列。将操作放入队列后,检查特定帐户是否在主帐户队列中。如果是这样,从那里提取它并将其提交给处理器。然后它执行自己队列中的所有动作,最后将自己返回到主队列。

这两个操作都需要锁定整个队列池。

【讨论】:

  • 一个队列用于帐户,一个用于操作,每个帐户都有另一个队列,其中包含只有该帐户才能执行的操作。所以总共3个队列。我会那样做谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-14
  • 2015-11-09
  • 1970-01-01
  • 1970-01-01
  • 2011-02-14
  • 2023-03-21
相关资源
最近更新 更多