如何在不使用foreach的情况下迭代Dictionary

时间:2010-01-01 23:06:15

标签: c# dictionary enumeration loops

我不确定这个标题是否很好地表达了对不起。

我基本上有一堆列出通信目标的元素。我将它们放在字典中虽然我愿意将它们移动到不同的数据结构中。我的问题是我有一个树状结构,其中一个键是一个分支,每个分支有很多叶子。分支和叶子都有以字符串形式存储的名称(不能是数字)。

private Dictionary < string, string[]> targets;

对于字典中的每个元素,我必须发送一个通信,当目标应答时,我会转到下一个目标并重新开始。所以在搜索之后我面临着这些困境:

  • 我不能使用通常的foreach因为我需要将指针保留在内存中以便在线程之间传递它。
  • 由于词典是随机访问,因此很难保留指针
  • 当我收到通讯时,我必须验证原点是否来自目标,所以我喜欢使用dictionary.contains方法。

我在C#上相当新,所以答案可能很明显,但我发现很难找到符合我需求的数据结构。什么是最简单的解决方案?有人可以提出什么建议吗?

谢谢。


修改

我认为我的帖子让很多人感到困惑,而且他们有点卡在指针和线程上。通过线程我并不意味着它们是并行的,只是因为我不能使用 foreach 或循环作为下一个迭代的下一个线程由传入通信触发。此机制目前无法更改,只需迭代即可。通过指针,我没有提到C中经常使用的内存指针,我只想指出你在列表中的位置。对不起,我是一名Java程序员,所以我可能会使用令人困惑的条款。

我注意到 Enumerator 经常被继承,并且可以与Dictionary和Linked List等结构一起使用。我找到了关于这个子结构被封装的例子,并以 foreach 循环为例。

是否可以以某种方式使用 GetEnumerator (),即使通过不同的线程访问,枚举器也会记住当前位置?

我要自己测试这些,但是如果有经验的人提供的任何意见总是受到赞赏!

4 个答案:

答案 0 :(得分:3)

您可以使用Parallel.ForEach方法(来自.NET 4)并行枚举此内容。它已作为Rx Framework的一部分向后移植,以便在.NET 3.5sp1中使用。

注意 - 这实际上并不是每个项目使用一个线程,而是根据您正在执行的系统的硬件线程数(通常更好......)使用线程池对工作进行分区。 。在.NET 4中,它利用了ThreadPool的新爬山和工作窃取算法,因此非常有效。

答案 1 :(得分:3)

我认为你需要重新设计一下你的架构,字典本身可能不是你需要用于有序迭代的数据结构。

我会考虑将您的树移动到链接列表中。

当您启动通信时,我建议让您的线程回调一个委托来更新您的列表数据,或者另一个共享数据结构,以跟踪您在通信过程中的位置。

static LinkedList<LeafItem> TreeList = new LinkedList<LeafItem>( );
foreach (LeafItem li in TreeList) {
    Thread newThread = new Thread(
                new ParameterizedThreadStart(Work.DoWork));
    newThread.Start(li);
    }

答案 2 :(得分:0)

编辑:来自您的评论:

  

我的算法更像是:得到   第一个目标发送消息给   第一个目标线程DIES - 抓住一个   港口接待事件检查是否是   正确的目标做一些行动 - 去   下一个目标开始循环。

如果要异步处理项目但并行处理,则应该能够通过将字典的键复制到Queue<string>并将两者都传递给处理的回调来实现这一点。你的异步响应。

您的完成处理程序pseduo-code可能如下所示:

// first extract your dictionary, key, and queue from whatever state 
// object you're using to pass data back to the completion event

if (dictionary.Contains(key)) {
    // process the response
}
if (queue.Count > 0) {
    string   key      = queue.Dequeue();
    string[] messages = dictionary[key];
    // send the messages, along with your state data and this callback
}

答案 3 :(得分:0)

这是一个轻微的远射,我怀疑我在这里弄乱了它:/

基本上我们的想法是为您的字典创建自定义IEnumerator。这个想法是它包含一个静态变量,用于保持枚举的“位置”,以便继续。

以下是一些可用于暂停和重新启动的框架代码。

public class MyDictEnumerator<T> : IEnumerator<T>
{
    private List<T> Dict;
    private static int curLocation = -1;

    public MyDictEnumerator(List<T> dictionary)
    {
        Dict = dictionary;
    }

    public T Current
    {
        get { return Dict[curLocation]; }
    }

    public void Dispose()
    { }

    object System.Collections.IEnumerator.Current
    {
        get { return Dict[curLocation]; }
    }

    public bool MoveNext()
    {
        curLocation++;
        if (curLocation >= Dict.Count)
            return false;
        return true;
    }

    public void Reset()
    {
        curLocation = -1;
    }
}

然后使用:

            MyDictEnumerator<KeyValuePair<string, int>> enumer = new MyDictEnumerator<KeyValuePair<string, int>>(test.ToList());

        while (enumer.MoveNext())
        {
            Console.WriteLine(enumer.Current.Value);
        }

我承认这不是最干净的方式。但是如果你打破了枚举器,并在另一个线程上创建一个新的,那么它将继续在同一点(我认为:/)

我希望这会有所帮助。