我在以下程序中遇到难以重现的错误,其中多个线程并行更新并发字典,并且主线程在固定时间间隔后按排序顺序显示字典的状态,直到所有更新线程完成。
public void Function(IEnumerable<ICharacterReader> characterReaders, IOutputter outputter)
{
ConcurrentDictionary<string, int> wordFrequencies = new ConcurrentDictionary<string, int>();
Thread t = new Thread(() => UpdateWordFrequencies(characterReaders, wordFrequencies));
bool completed = false;
var q = from pair in wordFrequencies orderby pair.Value descending, pair.Key select new Tuple<string, int>(pair.Key, pair.Value);
t.Start();
Thread.Sleep(0);
while (!completed)
{
completed = t.Join(1);
outputter.WriteBatch(q);
}
}
该函数有一个字符流列表和一个输出器。该函数维护从每个字符流(并行)读取的单词的单词频率的并发字典。单词被一个新线程读入,主线程每 1 毫秒输出字典的当前状态(按排序顺序),直到所有输入流都被读取(实际上输出将类似于每 10 秒,但错误似乎只出现在非常小的值上)。WriteBatch 函数只是写入控制台:
public void WriteBatch(IEnumerable<Tuple<string, int>> batch)
{
foreach (var tuple in batch)
{
Console.WriteLine("{0} - {1}", tuple.Item1, tuple.Item2);
}
Console.WriteLine();
}
大多数执行都很好,但有时我在 WriteBatch 函数的 foreach 语句中收到以下错误:
“未处理的异常:System.ArgumentException:索引等于或大于数组的长度,或者字典中的元素数大于从索引到目标数组末尾的可用空间。”
如果主线程在启动更新线程之后和开始显示循环之前休眠一小会儿,错误似乎确实消失了。如果删除 orderby 子句并且字典未在 linq 查询中排序,它似乎也消失了。有什么解释吗?
WriteBatch 函数中的foreach (var tuple in batch)
语句给出了错误。堆栈跟踪如下:
未处理的异常:System.ArgumentException:索引等于或大于数组的长度,或者字典中的元素数大于从索引到目标数组末尾的可用空间。在 System.Collections.Concurrent.ConcurrentDictionary2.System.Collections.Generic.ICollection>.CopyTo(KeyValuePair2[] 数组,Int32 索引) 在 System.Linq.Buffer1..ctor(IEnumerable1 源) 在 System.Linq.OrderedEnumerable1。 d__0.MoveNext() at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext() at MyProject.ConsoleOutputter.WriteBatch(IEnumerable1 batch) in C:\MyProject\ConsoleOutputter.cs:line 10 at MyProject.Function(IEnumerable1 characterReaders, IOutputter outputter)