在排序和显示时在并发字典中获取参数异常 [英] getting argument exception in concurrent dictionary when sorting and displaying as it is being updated

查看:250
本文介绍了在排序和显示时在并发字典中获取参数异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下程序中,我遇到一个难以重现的错误,其中多个线程并行更新并发字典,主线程在固定时间间隔后以排序顺序显示字典的状态,直到所有更新线程完成。

I am getting a hard to reproduce error in the following program in which a number of threads update a concurrent dictionary in parallel and the main thread displays the state of the dictionary in sorted order after fixed time intervals, until all updating threads complete.

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函数只是写入控制台:

The function is given a list of character streams and an outputter. The function maintains a concurrent dictionary of word frequencies of words read from each of the character streams (in parallel). The words are read in by a new thread, and the main thread outputs the current state of the dictionary (in sorted order) every 1 miliseconds until all the input streams have been read (in practice the outputting will be something like every 10 seconds, but the error only seems to be appearing for very small values). The WriteBatch function just writes to the console:

public void WriteBatch(IEnumerable<Tuple<string, int>> batch)
{
    foreach (var tuple in batch)
    {
        Console.WriteLine("{0} - {1}", tuple.Item1, tuple.Item2);
    }
    Console.WriteLine();
}

大多数执行都很好,但有时候我在foreach语句在WriteBatch函数中:

Most executions are fine, but sometimes I get the following error at the foreach statement in the WriteBatch function:

未处理的异常:System.ArgumentException:索引等于或大于
比数组的长度,元素在字典中的位置比从索引到目标数组结尾的可用空间是gre

"Unhandled Exception: System.ArgumentException: The index is equal to or greater than the length of the array, or the number of elements in the dictionary is gre ater than the available space from index to the end of the destination array."

如果主线程在开始更新线程之后和开始显示循环之前休眠一段时间,错误似乎消失了。如果删除orderby子句并且在linq查询中没有排序字典,它似乎也消失了。任何解释?

The error does seem to go away if the main thread sleeps for a short while after starting the updating threads and before starting the display loop. It also seems to go away if the orderby clause is removed and the dictionary is not sorted in the linq query. Any explanations?

WriteBatch函数中的 foreach(var tuple批处理)语句会给出错误。堆栈跟踪如下:

The foreach (var tuple in batch) statement in the WriteBatch function gives the error. The stack trace is as follows:

未处理的异常:System.ArgumentException:索引等于或大于
比数组的长度或数字的字典中的元素是从索引到目标数组结尾的可用空间的gre

在System.Collections.Concurrent.ConcurrentDictionary2.System.Collections.Ge
neric.ICollection> .CopyTo(K
eyValuePair2 []数组,Int32索引)
在System.Linq .Buffer1..ctor(IEnumerable1 source)
在System.Linq.OrderedEnumerable1.d__0.MoveNext()
在System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext()
在MyProject.ConsoleOutputter。在MyProject.Function(IEnumerable1 characterReaders,IOutputter outputter)

Unhandled Exception: System.ArgumentException: The index is equal to or greater than the length of the array, or the number of elements in the dictionary is gre ater than the available space from index to the end of the destination array. at System.Collections.Concurrent.ConcurrentDictionary2.System.Collections.Ge neric.ICollection>.CopyTo(K eyValuePair2[] array, Int32 index) at System.Linq.Buffer1..ctor(IEnumerable1 source) at 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)

推荐答案

如其他人所说,在内部类 System.Linq.Buffer< T> 的构造函数中有一个竞争, OrderBy

As others have said, there is a race in the constructor of the internal class System.Linq.Buffer<T>, which is called by OrderBy.

这是违规的代码段:

TElement[] array = null;
int num = 0;
if (collection != null)
{
    num = collection.Count;
    if (num > 0)
    {
        array = new TElement[num];
        collection.CopyTo(array, 0);
    }
}

添加项目时抛出异常在调用 collection.Count 之后但在调用之前调用集合

The exception is thrown when item(s) are added to the collection after the call to collection.Count but before the call to collection.CopyTo.

作为解决方法,您可以创建字典的快照副本

As a work around, you can make a "snapshot" copy of the dictionary before you sort it.

您可以通过调用 ConcurrentDictionary.ToArray。 ConcurrentDictionary 类中实现它是安全的。

You can do this by calling ConcurrentDictionary.ToArray.
As this is implemented in the ConcurrentDictionary class itself, it is safe.

使用这种方法意味着你不必保护

Using this approach means you don't have to protect the collection with a lock which, as you say, defeats the purpose of using a concurrent collection in the first place.

while (!completed)
{
    completed = t.Join(1);

    var q =
      from pair in wordFrequencies.ToArray() // <-- add ToArray here
      orderby pair.Value descending, pair.Key
      select new Tuple<string, int>(pair.Key, pair.Value);

    outputter.WriteBatch(q);
}            

这篇关于在排序和显示时在并发字典中获取参数异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆