代码之家  ›  专栏  ›  技术社区  ›  Jerry

使用线程对C#事件中的循环进行计数

  •  2
  • Jerry  · 技术社区  · 17 年前

    我有一个列表视图控件,快把我逼疯了。这是一个多选列表框,因此如果用户选择5000行,然后通过选择一行来取消选择,SelectedIndexChanged将触发5001次。这会导致我的应用程序挂起。

    我试图使用线程来计算事件将被触发的次数,然后让最后一次迭代完成所有实际工作。

    这是我开始使用的代码。 最大的收获: 由于我无法控制的项目,我需要“做花哨的计算”与调用事件处于同一线程中。

    编辑:I 知道

    我最大的问题不是创建线程。我的“做幻想”必须在同一条线上。

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

    5 回复  |  直到 17 年前
        1
  •  2
  •   Matt Brunell    17 年前

    我记得解决了这个问题 before :

    项目选择更改处理程序。说-- 50毫秒。使用计时器,一旦选择 选择更改了多次 在延迟期内,则 原始被忽略,但之后 延迟已过期,逻辑是 执行。

    这样地:

    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");
    }
    }
    
        2
  •  1
  •   Craig Gidney Mihai    17 年前

    一个可能的解决方案是延迟工作,这样你就知道是否有更多的事件发生。这假设选择的顺序并不重要;重要的是现状。

    与其在事件触发后立即执行工作,不如设置一个计时器,在事件触发几毫秒后执行。如果计时器已经在运行,则什么都不做。这样,用户应该不会察觉到任何差异,但动作不会挂起。

    你也可以在另一个线程上完成这项工作,但要有一个标志来指示工作正在完成。如果选择事件触发时,工作仍在进行中,则设置一个标志,指示应重复该工作。将“repeat_work”设置为真5000次并不昂贵。

        3
  •  1
  •   Michael Meadows    17 年前

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

    我建议尽可能避免创建线程,因为它们有开销。从你的例子中,我看不出有什么需要并行的地方。

        4
  •  0
  •   casperOne    17 年前

    首先,虽然您正在正确同步对PaintQueue的访问,但我觉得在这种情况下,这更多的是偶然的,而不是设计的。如果您有其他代码在其他线程上访问PaintQueue,那么您就有问题了。

    其次,这段代码没有意义。您正在暂存一个新线程,递增该线程上的值,然后等待1/10秒。问题是,启动线程的代码正在等待该线程完成。因此,你只是在UI线程中等待什么。

    即使你将SelectedIndexChange事件排队,你也无法阻止你的应用程序挂起。SelectedIndexChange事件将在每次选择项目时触发,如果用户选择5000个项目,则需要处理所有5000个事件。你可以给他们一个窗口(每n秒或其他时间处理一次),但这是相当武断的,你给用户设置了一个计时器,这很糟糕。

    您应该做的是不要将操作与SelectedIndexChanged事件绑定。相反,让用户选择项目,然后让他们执行一些其他操作(例如单击按钮),这些操作将对所选项目生效。

    如果你必须在UI线程上长时间处理多个项目,你的应用程序仍然会挂起,但至少选择这些项目不会挂起。

        5
  •  0
  •   Brian Rasmussen    17 年前

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