2009-01-22 44 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

通过启动一个新线程,然后立即加入它,你并没有真正实现任何类型的并发。上面代码的唯一“效果”是你的方法由另一个线程运行。

此外,如果您想要使用后台线程和安全的新线程的相当昂贵的成本,您应该使用线程池。