使用线程来统计C#事件中的循环
编辑:它不是一个列表框。我的错。这是一个列表视图。使用线程来统计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...
}
我记得解决这一问题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");
}
}
这基本上是我在做什么(不是在我上面的示例hack中,而是在我真实的代码中)。我产生了一个线程并让它休眠。问题是其中一个控制(我无法控制)要求这些数据与它自己在同一个线程中。 – Jerry 2009-01-22 18:21:36
首先,当你在正确的同步访问PaintQueue,我觉得在这种情况下是一个偶然的机会更多,而不是设计。如果您有其他代码在其他线程上访问PaintQueue,那么您遇到了问题。
其次,这段代码没有意义。您正在后台创建一个新线程,递增该线程上的值,然后等待1/10秒。事情是,启动线程的代码正在等待该线程完成。正因为如此,你只是在UI线程中等待一无所获。
即使您对SelectedIndexChange事件进行排队,您也无法阻止挂起应用程序。 SelectedIndexChange事件将在每次选择项目时触发,并且如果用户选择5000个项目,则需要处理所有5000个事件。你可以给他们一个窗口(每n秒或任何其他处理),但这是非常随意的,你把用户放在一个定时器上,这是一个坏的。
你应该做的是不将操作绑定到SelectedIndexChanged事件。相反,请让用户选择这些项目,然后让他们执行一些其他操作(例如,单击一个按钮),这将对所选项目起作用。
如果您必须在UI线程上处理很长一段时间的项目,但您的应用程序仍然会挂起,但至少选择项目不会挂起。
一个可能的解决方案是延迟工作,所以你知道是否有更多的事件发生。这假定选择顺序并不重要;所有重要的是现状。
一旦事件触发,不要立即开展工作,而是设置一个计时器,在事件触发几毫秒后完成。如果计时器已经在运行,则什么也不做。用这种方式用户应该没有区别,但行动不会挂起。
您也可以在另一个线程上完成工作,但有一个标志指示正在完成工作。如果当选择事件触发时,工作仍在进行中,请设置一个标志,指示应该重复该工作。将'repeat_work'设置为true 5000次并不昂贵。
我得到你正在试图解决通过暴力问题的印象。我建议尝试不同的事件:
private void myListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
if (e.IsSelected)
{
// do your logic here
}
}
我会建议避免创建线程,如果可能的话,因为他们有overheaad。我从你的例子看不出有什么需要并行的地方。
通过启动一个新线程,然后立即加入它,你并没有真正实现任何类型的并发。上面代码的唯一“效果”是你的方法由另一个线程运行。
此外,如果您想要使用后台线程和安全的新线程的相当昂贵的成本,您应该使用线程池。
你在使用什么平台?这是一个Windows窗体应用程序? – 2009-01-22 18:03:14
它不能是winforms,因为windows窗体中的ListBox控件的行为不像解释的那样。选择单个项目以取消选择倍数只会触发一个SelectedIndexChanged事件。 – 2009-01-22 18:08:51
是的。它是winForms。我编辑了这篇文章。感谢您的支持。 – Jerry 2009-01-22 18:12:11