【问题标题】:Thread-Safe Random Array线程安全的随机数组
【发布时间】:2018-10-15 21:19:18
【问题描述】:

有没有办法在不使用lock() 或Concurrent* 方法集的情况下创建随机数组/列表/哈希集?

我的目标:

  • 能够随意添加和删除字符串(或者能够添加和“禁用”字符串)
  • 能够清除列表或换出整组字符串
  • 能够从中获取项目(类似于 TryPeek)以随机选择任何项目,而无需使用 .ElementAt 与 .Count/.Count() 随机的混合

我正在尝试创建一个系统,以完全随机地从集合中选择一个代理,该集合可由另一个线程修改,可以随时删除或添加新代理。

这里有一些“随机化”解决方案,以及为什么它们不好,不应该使用

.ElementAt(Random.Next(List.Count)) 不是随机化列表的好方法

由于多种原因,这非常不适合多线程场景。 即使使用 lock(){} 包裹它以及与集合有关的所有其他代码也可能导致修改后的枚举发生冲突。 我的意思是,List 可以更改其计数(可能更低),从而导致 .ElementAt 在选择时导致错误,可以说是最终元素,但最终元素只是在 ElementAt 到达它之前从列表中删除,导致异常。

.OrderBy(随机)

也是另一种不好的方法,因为它会在选择元素之前随机化整个列表,并且在执行 OrderBy 时容易修改 Collection 导致异常。

两种从列表中随机选择一项的坏方法都可以通过在 .OrderBy(Random) 之前或在执行 .ElementAt 之前简单地“解决”.ToArray() 来“解决”,但您还必须使用 ToArray( ) 对 .ElementAt 的随机计数。 这里的问题也是,这将对内存不利,具体取决于您所做的工作实际上会使列表的内存使用量翻倍。

这就是为什么我要问是否有任何方法可以有效地随机化,而不会使多线程与对集合的修改发生冲突。

当前代码

lock = new object();
...
lock (_Lock) {
    proxy = proxyArray.ElementAt(proxyArray.Count == 1 ? 0 : rand.Next(0, proxyArray.Count - 1));
}

我正在计数 == 1 ? 0,这样它甚至不会为了一个明显的答案而浪费 CPU 随机化。

我对 ConcurrentBag 的尝试

object proxiesRefill = new object();//for lock
...
while (!concurrentProxiesBag.TryTake(out proxy)) {//keep attempting until it takes a proxy
    lock (proxiesRefill) {//it failed possibly because its empty lets lock and check on 1 thread
        if (concurrentProxiesBag.IsEmpty) {//if its empty lets refill
            concurrentProxiesBag = new ConcurrentBag<string>(hashsetDisabledProxies);//Refill by creating a new ConcurrentBag and adding all disabled proxies to it [Properly threadsafe? not sure]
        }
    }
    Thread.Sleep(100);//sleep for 100ms just to cool down a small bit
}
//Got proxy
hashsetDisabledProxies.Add(proxy);//Disable proxy as its taken out of the active proxy bag by TryTake
//Its a hashset for no duplicates that could possibly get added.

【问题讨论】:

  • 如果您想要无锁解决方案,您可以尝试ImmutableList&lt;T&gt; 和ImmutableHashSet&lt;T&gt; - 查看更多关于Immutable Collections 的信息。然后您的集合将是不可变的,因此是线程安全的,任何更改都将创建一个新集合,该集合可以原子地交换为旧集合,但内存使用量比仅仅复制集合要低。
  • 为什么要使用 ElementAt() 而不仅仅是列表的索引器?
  • 我当前代码中的 @EtiennedeMartel proxyArray 是一个哈希集,所以我使用 .elementat。许多类似数组的对象没有像 [] 这样的索引器
  • 既然如此,你为什么不直接使用循环法?
  • 使用并发队列怎么样。从前面取出,如果您愿意与其他线程共享它或完成它,请立即再次将该项目排入队列。也许有一个类有两个方法 borrow() 和 return(),它对你的消费类隐藏了实现细节。

标签: c# multithreading


【解决方案1】:

我对您的代码做了一些更改:

while (!concurrentProxiesBag.TryTake(out proxy)) {//keep attempting until it takes a proxy
    if (concurrentProxiesBag.IsEmpty) {//if its empty lets refill
            concurrentProxiesBag = new ConcurrentBag<string>(hashsetDisabledProxies);//Refill by creating a new ConcurrentBag and adding all disabled proxies to it [Properly threadsafe? not sure]
        }
    }
    Thread.Sleep(100);//sleep for 100ms just to cool down a small bit

IsEmpty, Count, ToArray(), GetEnumerator()锁定整个结构;

【讨论】:

    猜你喜欢
    • 2019-08-09
    • 2020-01-31
    • 2011-08-14
    • 2020-06-20
    • 1970-01-01
    • 2012-02-07
    • 2014-10-16
    • 1970-01-01
    相关资源
    最近更新 更多