簡體   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