Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在更新时排序和显示时在并发字典中获取参数异常_C#_Multithreading_Concurrency_Race Condition_Concurrentdictionary - Fatal编程技术网

C# 在更新时排序和显示时在并发字典中获取参数异常

C# 在更新时排序和显示时在并发字典中获取参数异常,c#,multithreading,concurrency,race-condition,concurrentdictionary,C#,Multithreading,Concurrency,Race Condition,Concurrentdictionary,我在下面的程序中遇到了一个难以重现的错误,在这个程序中,许多线程并行更新一个并发字典,主线程在固定的时间间隔后按排序顺序显示字典的状态,直到所有更新线程完成 public void Function(IEnumerable<ICharacterReader> characterReaders, IOutputter outputter) { ConcurrentDictionary<string, int> wordFrequencies = new Concur

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

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);
    }            
}
public void函数(IEnumerable characterReaders,IOutputter OUTPUTER)
{
ConcurrentDictionary词频=新建ConcurrentDictionary();
线程t=新线程(()=>UpdateWordFrequencies(characterReaders,wordFrequencies));
bool completed=false;
var q=从wordFrequencies中的pair按pair.Value降序,pair.Key选择新元组(pair.Key,pair.Value);
t、 Start();
睡眠(0);
而(!已完成)
{
完成=t.连接(1);
输出写回(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();
}
public void写回(IEnumerable批处理)
{
foreach(批处理中的变量元组)
{
WriteLine(“{0}-{1}”,tuple.Item1,tuple.Item2);
}
Console.WriteLine();
}
大多数执行都是正常的,但有时在WriteBatch函数中的foreach语句中会出现以下错误:

“未处理的异常:System.ArgumentException:索引等于或大于 大于数组的长度,或者字典中的元素数为gre 大于从索引到目标数组末尾的可用空间。“

如果主线程在启动更新线程之后和启动显示循环之前休眠一段时间,则错误似乎会消失。如果删除orderby子句并且在linq查询中没有对字典进行排序,那么它似乎也会消失。有什么解释吗

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

未处理的异常:System.ArgumentException:索引等于或大于 大于数组的长度,或者字典中的元素数为gre 大于从索引到目标数组末尾的可用空间。 位于System.Collections.Concurrent.ConcurrentDictionary2.System.Collections.Ge neric.ICollection>.CopyTo(K eyValuePair2[]数组,Int32索引) 位于System.Linq.Buffer1..ctor(IEnumerable1源) 在System.Linq.OrderedEnumerable1.d_u0.MoveNext()中 在System.Linq.Enumerable.WhereSelectEnumerableInterator2.MoveNext()中 在C:\MyProject\consoleoutput.cs中的MyProject.consoleoutput.WriteBatch(IEnumerable1批处理)处:第10行
在MyProject.Function(IEnumerable1 characterReaders,IOutputter outputter)

上,在评论中与ChrisShain讨论后,得出的结论是,在打印字典之前,您应该获得互斥访问权限,可以使用
互斥体
语句

使用锁进行操作:

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

同样,假设您在类级别分配了一个
互斥对象。请参阅。

正如其他人所说,内部类
System.Linq.Buffer
的构造函数中存在竞争,该类由
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
之后,但在调用
collection.CopyTo
之前,将项添加到
集合时,会引发异常


作为一种解决方法,您可以在对字典进行排序之前制作字典的“快照”副本

您可以通过调用
ConcurrentDictionary.ToArray.

来实现这一点,因为这是在
ConcurrentDictionary
类本身中实现的,所以是安全的

使用这种方法意味着您不必使用锁来保护集合,正如您所说,锁首先会破坏使用并发集合的目的

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);
}            
while(!completed)
{
完成=t.连接(1);
变量q=

来自wordFrequencies.ToArray()中的pair//哪一行给出了异常?还有,是否有堆栈跟踪?你能发布它吗?我的直觉是,与几乎所有难以重现的并发问题一样,这里存在某种竞争条件。我认为问题是,当你浏览字典时,其他线程正在更改它。如果你想输出字典的status在某个时间点,您需要获取该时刻的静态快照并打印出来,而不是您对函数的引用。请尝试在
q.ToArray()上完成所有工作
而不是字典本身。我认为这可能会解决问题。如果它确实让我知道,我会将其作为答案发布,我只是不完全确定。@MichaelDmitryAzarkevich我对此表示怀疑。从ConcurrentDictionary上的文档中:“ConcurrentDictionary的所有公共和受保护成员都是线程安全的,可以从多个线程并发使用。“我已使用堆栈跟踪进行了更新。该异常发生在WriteBatch函数中的foreach语句中。是的,这是某种竞争,但我很想知道为什么会发生。使用ToArray()拍摄快照的想法很好,但这不会导致性能开销和不必要的复制吗?在显示时只对整个并发字典进行同步怎么样?谢谢Michael,Shain。同步似乎是一种方法,但这不是完整的解决方案,对吗?loc
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);
}