2009-01-22 40 views
2

編輯:它不是一個列表框。我的錯。這是一個列表視圖。使用線程來統計C#事件中的循環

我有一個列表視圖控件,這讓我瘋狂。這是一個多選列表框,所以如果用戶選擇5000行,然後通過選擇單行來取消選擇它們,則SelectedIndexChanged將觸發5001次。這會導致我的應用程序掛起。

我正在嘗試使用線程來統計事件已經觸發的次數,然後讓最後一次迭代完成所有實際的工作。

這是我開始的代碼。 大問題:由於項目超出我的控制範圍,我需要「做花哨的計算」與呼叫事件處於同一線程。

編輯:我知道,這段代碼不起作用。 Join()阻止當前線程,否定了創建線程的全部目的。我的問題是:我如何做這樣的事情。

我最大的問題是沒有創建線程。這就是我的「看中」必須在同一個線索。

void IncrPaintQueue() 
    { 
     PaintQueue++; 
     Thread.Sleep(100); 
    } 

    int PaintQueue = 0; 

    private void SegmentList_SelectedIndexChanged(object sender, EventArgs e) 
    { 
     // We need to know how many threads this may possibly spawn. 
     int MyQueue = PaintQueue; 

     // Start a thread to increment the counter. 
     Thread Th = new Thread(IncrPaintQueue); 
     Th.IsBackground = true; 
     Th.Start(); 
     Th.Join(); 

     // if I'm not the last thread, then just exit. 
     // The last thread will do the right calculations. 
     if (MyQueue != PaintQueue - 1) 
      return; 

     // Reset the PaintQueue counter. 
     PaintQueue = 0; 

     // ... do fancy calculations here... 
    } 

+0

你在使用什麼平臺?這是一個Windows窗體應用程序? – 2009-01-22 18:03:14

+0

它不能是winforms,因爲windows窗體中的ListBox控件的行爲不像解釋的那樣。選擇單個項目以取消選擇倍數只會觸發一個SelectedIndexChanged事件。 – 2009-01-22 18:08:51

+0

是的。它是winForms。我編輯了這篇文章。感謝您的支持。 – Jerry 2009-01-22 18:12:11

回答

2

我記得解決這一問題before

也許你會 把一個最小的延遲在 ItemSelectionChange處理器更好的辦法。說 - 50ms。使用計時器,一旦選擇 更改,重新啓動計時器。如果 選擇在延遲期內改變了多次 ,則 原始值將被忽略,但在 延遲期滿後,將執行邏輯 。

像這樣:

public class SelectionEndListView : ListView 
{ 
private System.Windows.Forms.Timer m_timer; 
private const int SELECTION_DELAY = 50; 

public SelectionEndListView() 
{ 
    m_timer = new Timer(); 
    m_timer.Interval = SELECTION_DELAY; 
    m_timer.Tick += new EventHandler(m_timer_Tick); 
} 

protected override void OnSelectedIndexChanged(EventArgs e) 
{ 
    base.OnSelectedIndexChanged(e); 

    // restart delay timer 
    m_timer.Stop(); 
    m_timer.Start(); 
} 

private void m_timer_Tick(object sender, EventArgs e) 
{ 
    m_timer.Stop(); 

    // Perform selection end logic. 
    Console.WriteLine("Selection Has Ended"); 
} 
} 
+0

這基本上是我在做什麼(不是在我上面的示例hack中,而是在我真實的代碼中)。我產生了一個線程並讓它休眠。問題是其中一個控制(我無法控制)要求這些數據與它自己在同一個線程中。 – Jerry 2009-01-22 18:21:36

0

首先,當你在正確的同步訪問PaintQueue,我覺得在這種情況下是一個偶然的機會更多,而不是設計。如果您有其他代碼在其他線程上訪問PaintQueue,那麼您遇到了問題。

其次,這段代碼沒有意義。您正在後臺創建一個新線程,遞增該線程上的值,然後等待1/10秒。事情是,啓動線程的代碼正在等待該線程完成。正因爲如此,你只是在UI線程中等待一無所獲。

即使您對SelectedIndexChange事件進行排隊,您也無法阻止掛起應用程序。 SelectedIndexChange事件將在每次選擇項目時觸發,並且如果用戶選擇5000個項目,則需要處理所有5000個事件。你可以給他們一個窗口(每n秒或任何其他處理),但這是非常隨意的,你把用戶放在一個定時器上,這是一個壞的。

你應該做的是不將操作綁定到SelectedIndexChanged事件。相反,請讓用戶選擇這些項目,然後讓他們執行一些其他操作(例如,單擊一個按鈕),這將對所選項目起作用。

如果您必須在UI線程上處理很長一段時間的項目,但您的應用程序仍然會掛起,但至少選擇項目不會掛起。

1

一個可能的解決方案是延遲工作,所以你知道是否有更多的事件發生。這假定選擇順序並不重要;所有重要的是現狀。

一旦事件觸發,不要立即開展工作,而是設置一個計時器,在事件觸發幾毫秒後完成。如果計時器已經在運行,則什麼也不做。用這種方式用戶應該沒有區別,但行動不會掛起。

您也可以在另一個線程上完成工作,但有一個標誌指示正在完成工作。如果當選擇事件觸發時,工作仍在進行中,請設置一個標誌,指示應該重複該工作。將'repeat_work'設置爲true 5000次並不昂貴。

1

我得到你正在試圖解決通過暴力問題的印象。我建議嘗試不同的事件:

private void myListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e) 
{ 
    if (e.IsSelected) 
    { 
     // do your logic here 
    } 
} 

我會建議避免創建線程,如果可能的話,因爲他們有overheaad。我從你的例子看不出有什麼需要並行的地方。

0

通過啓動一個新線程,然後立即加入它,你並沒有真正實現任何類型的併發。上面代碼的唯一「效果」是你的方法由另一個線程運行。

此外,如果您想要使用後臺線程和安全的新線程的相當昂貴的成本,您應該使用線程池。