繁体   English   中英

如何在不使用foreach的情况下遍历Dictionary

[英]How to iterate through Dictionary without using foreach

我不确定标题是否写得很好,很抱歉。

我基本上有一堆列出通信目标的元素。 尽管我愿意将它们移至其他数据结构,但仍将它们放入字典中。 我的问题是我有一个树状结构,其中一个键是一个分支,每个分支都有很多叶子。 分支和叶子的名称都存储在字符串中(不能为数字)。

private Dictionary < string, string[]> targets;

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

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

我是C#的新手,所以答案可能很明显,但是我很难找到适合自己需求的数据结构。 什么是最简单的解决方案? 有人可以建议什么吗?

谢谢。


编辑

我认为我的帖子使很多人感到困惑,并且它们被卡在术语指针和线程上。 我所说的线程并不是指它们是并行的,只是我不能使用foreach或循环,因为执行下一次迭代的下一个线程是由传入通信触发的。 此机制目前无法更改,仅必须更改。 通过指针,我并不是指经常在C语言中使用的内存指针,我的意思是指向您在列表中的位置的东西。 抱歉,我是Java程序员,所以我可能会使用令人困惑的术语。

我注意到枚举器通常是继承的,并且可以与诸如Dictionary和Linked List之类的结构一起使用。 我发现了一些有关封装此子结构的示例,并以foreach循环为例。

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

我打算自己进行测试,但是如果有经验的人提出任何建议,我们将不胜感激!

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

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

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

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

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

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

编辑:从您的评论:

我的算法更像是:获取第一个目标将消息发送到第一个目标线程DIES-捕获端口接收事件,检查其正确的目标是否执行了某些操作-转到下一个目标,开始循环。

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

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

// 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
}

这是一个有点远的镜头,我怀疑我已经把它弄乱了:/

基本上,这个想法是为字典创建一个自定义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);
        }

我承认这不是最干净的方法。 但是,如果您脱离枚举器,并在另一个线程上创建一个新的枚举器,则它将在同一点继续(我认为:/)

我希望这有帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM