对于大专家来说,我有一个具有挑战性的问题。这在我的代码中还没有实际应用,但来自我刚才的想法。
如果我有一个IList<T>
,我如何实现一个随机遍历列表并且可以同时被多个线程使用的枚举器?
例如,如果我有元素A
,B
,C
,D
,E
,F
和两个并发线程执行获取ReaderLock的列表上的for-each
循环(所以我确定没有其他人会触摸列表因此导致异常),我希望他们各自的周期返回,例如B
, E
,C
,D
,A
,F
和E
,B
,D
,{{ 1}},C
,A
。
我之所以需要这个,是因为我需要在F
个元素上放置锁,以便向客户端because SslStream is not thread-safe发送数据。随机挑选元素(但确保我选择全部)可以减少锁定冲突概率,并且可以提高I / O绑定操作性能。
请记住,即使我告诉你为什么我需要这样的调查员,我仍然喜欢挑战。可以有其他方法将相同的数据发送到多个客户端,但我的问题仍然是相同的:):)
答案 0 :(得分:5)
这样的东西(显然需要生产):
class RandomList<T> : IEnumerable<T> {
private readonly IList<T> list;
private readonly Random rg;
private readonly object sync = new Object();
public RandomList(IList<T> list) : this(list, new Random()) { }
public RandomList(IList<T> list, Random rg) {
Contract.Requires<ArgumentNullException>(list != null);
Contract.Requires<ArgumentNullException>(rg != null);
this.list = list;
this.rg = rg;
}
public IEnumerator<T> GetEnumerator() {
List<int> indexes;
// Random.Next is not guaranteed to be thread-safe
lock (sync) {
indexes = Enumerable
.Range(0, this.list.Count)
.OrderBy(x => this.rg.Next())
.ToList();
}
foreach (var index in indexes) {
yield return this.list[index];
}
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
答案 1 :(得分:3)
创建一个与列表大小相同的数组,最初将其填充为[i] = i,然后使用Fisher Yates algorithm进行随机播放。
然后,您的枚举器可以迭代此数组,从提供的随机索引返回源列表中的元素。
答案 2 :(得分:0)
这应该这样做:
public static IEnumerable<T> YieldRandom<T>(this IList<T> items) {
Random random = new Random();
HashSet<int> yielded = new HashSet<int>(items.Count);
for (int i=0; i<items.Count; i++) {
// find an index we haven't yielded yet
int yieldIndex;
do {
yieldIndex = random.Next(items.Count);
}
while (yielded.Contains(yieldIndex));
yielded.Add(yieldIndex);
yield return items[yieldIndex];
}
}
我确信更多的LINQ可以在某些地方使用:)