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

发布于 2024-08-17 02:10:17 字数 952 浏览 6 评论 0原文

我不确定标题是否表达得很好,所以抱歉。

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

private Dictionary < string, string[]> targets;

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

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

我对 C# 相当陌生,所以答案可能是显而易见的,但我发现很难找到适合我需求的数据结构。最简单的解决方案是什么?有人可以建议什么吗?

谢谢。


编辑

我认为我的帖子让很多人感到困惑,他们有点停留在指针和线程这两个术语上。我所说的线程并不是说它们是并行的,只是我不能使用 foreach 或循环,因为执行下一次迭代的下一个线程是由传入通信触发的。这个机制暂时不能改变,只能迭代。我所说的指针并不是指 C 中经常使用的内存指针,而是指指向列表中位置的指针。抱歉,我是一名 Java 程序员,所以我可能会使用令人困惑的术语。

我注意到枚举器通常是继承的,并且它可以与字典和链接列表等结构一起使用。我发现关于这个子结构被封装的示例,并显示了 foreach 循环作为示例。

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

我将自己测试这些,但如果有经验丰富的人提供任何意见,我们将不胜感激!

I am not sure if the title formulates it well so sorry.

I basically have a bunch of elements listing targets for a communication. I placed them in a dictionary though i am open to moving them to a different data structure. My problem is that i have a tree-like structure where a key is a branch and each branch has many leaves. Both the branch and the leaves have names stored in strings (cannot be numeral).

private Dictionary < string, string[]> targets;

For each element in the dictionary i must send a communication, and when the target answers i go to the next target and start over. So after searching i am faced with these dilemmas:

  • I cannot use the usual foreach because i need to keep the pointer in memory to pass it in between threads.
  • Since dictionaries are random access it is difficult to keep a pointer
  • When i receive a communication i must verify if the origins are from a target, so i like the dictionary.contains method for that.

I am fairly new at C#, so the answer is probably obvious but i am finding a hard time finding a data structure that fits my needs. What would be the simplest solution? Can somebody suggest anything?

Thank you.


EDIT

I think my post has confused many, and they are sort of stuck on the terms pointers and threads. By threads i don`t mean that they are parallel, simply that i cannot use a foreach or a loop as the next thread that does the next iteration is triggered by incoming communication. This mechanism cannot be changed at the moment, just the iteration must be. By pointer i wasn't referring to the memory pointers often used in C, i just meant something that points to where you are in a list. Sorry i am a Java programmer so i might be using confusing terms.

I noticed the Enumerator is often inherited and that it can be used with structures such as Dictionary and Linked List. Examples i find talk about this sub structure being encapsulated, and shows foreach loops as examples.

Would it be possible to use GetEnumerator() in some way that the enumerator would remember the current position even when accessed through a different thread?

I am off to test these on my own, but if any input from more experienced people is always appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

烟花肆意 2024-08-24 02:10:17

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

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

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

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

I think you need to re-work your architecture a bit, the Dictionary itself is probably not the data structure you need to use for a ordered iteration.

I would consider moving your tree into a linked list instead.

When you kick off your communications I would suggest having your threads callback a delegate to update your list data, or another shared datastructure that keeps track of where you are in the communication process.

static LinkedList<LeafItem> TreeList = new LinkedList<LeafItem>( );
foreach (LeafItem li in TreeList) {
    Thread newThread = new Thread(
                new ParameterizedThreadStart(Work.DoWork));
    newThread.Start(li);
    }
谎言月老 2024-08-24 02:10:17

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

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

You can enumerate over this in parallel using Parallel.ForEach method (from .NET 4). It has been backported as part of the Rx Framework for use in .NET 3.5sp1.

Note - this doesn't actually use one thread per item, but rather partitions the work using the thread pool, based on the hardware thread count of the system on which you're executing (which is usually better...). In .NET 4, it takes advantage of the ThreadPool's new hill climbing and work stealing algorithms, so is very efficient.

彩虹直至黑白 2024-08-24 02:10:17

这个有点远,我怀疑我在这里的某个地方搞砸了:/

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

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

我希望这会有所帮助。

this one is a slight long shot, and I suspect I've messed it up somewhere here :/

basically the idea is to create a custom IEnumerator for your dictionary. The idea being that it contains a static variable that keeps the "location" of the enumeration, for continuing.

the following is some skeleton code for something that does work for pausing and restarting.

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

Then to use:

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

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

I'll admit that this isn't the cleanest way of doing it. But if you break out of the enumerator, and create a new one on another thread, then it will continue at the same point (i think :/)

I hope this helps.

淡淡の花香 2024-08-24 02:10:17

编辑:根据您的评论:

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

如果您想要异步处理项目,但并行处理,您应该能够通过将字典的键复制到 Queue 并将两者传递给处理异步响应的回调。

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

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

Edit: from your comments:

My alogrithm is more like: Get the
first target Send the message to the
first target Thread DIES - Catch a
port reception event check if its the
right target do some actions - go to
the next target start the loop over.

If you want to process the items asynchronously but not in parallel, you should be able to achieve this by copying the dictionary's keys to a Queue<string> and passing both to the callback that handles your asynchronous responses.

Your completion handler pseduo-code might look like this:

// 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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文