使用线程计算C#事件中的循环

时间:2021-10-25 15:32:01

EDIT: It is not a listbox. My mistake. It is a list view.

编辑:它不是一个列表框。我的错。这是一个列表视图。

I have a list view control that's driving me nuts. It is a multi-select list box, so if the user selects 5000 rows, then de-selects them by selecting a single row, the SelectedIndexChanged fires 5001 times. This causes my app to hang.

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

I'm trying to use threads to count the number of times that the event WOULD have fired, and then letting the last iteration do all the actual work.

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

Here's the code I started with. The big catch: I need the "do fancy calculations" to be in the same thread as the calling events due to items out of my control.

这是我开始使用的代码。最重要的一点:我需要“做花哨的计算”与调用事件在同一个线程中,因为我控制的项目。

EDIT: I know that this code doesn't work. The Join() blocks the current thread which negates the entire purpose of creating the thread. My question is : How do I do something LIKE this.

编辑:我知道这段代码不起作用。 Join()阻塞当前线程,这会否定创建线程的整个目的。我的问题是:我怎么做这样的事情。

My biggest problem isn't creating the thread. It's that my "do fancy" has to be in the same thread.

我最大的问题是没有创建线程。这是我的“幻想”必须在同一个线程中。

    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 个解决方案

#1


I remember solving this issue before:

我记得之前解决过这个问题:

A better way perhaps for you would be to put a minimal delay in your ItemSelectionChange Handler. Say -- 50ms. Use a timer, Once the selection changes, restart the timer. If the selection changed more than once within the delay period, then the original is ignored, but after the delay has expired, the logic is executed.

对您而言,更好的方法是在ItemSelectionChange处理程序中放置最小延迟。说 - 50ms。使用计时器,选择更改后,重新启动计时器。如果选择在延迟时间内改变了不止一次,则原始被忽略,但在延迟期满后,执行逻辑。

Like this:

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


A possible solution is to delay the work, so you know whether or not more events have fired. This assumes the order of the selections is not important; all that matters is the current state.

一种可能的解决方案是延迟工作,因此您知道是否有更多事件被触发。这假设选择的顺序并不重要;重要的是当前的状态。

Instead of doing the work as soon as the event fires, set up a timer to do it a couple milliseconds after the event fires. If the timer is already running, do nothing. In this way the user should perceive no difference, but the actions will not hang.

事件触发后,不要在事件触发后立即执行工作,而是在事件触发后的几毫秒内设置一个计时器。如果计时器已在运行,则不执行任何操作。通过这种方式,用户应该感知没有区别,但操作不会挂起。

You could also do the work on another thread, but have a flag to indicate work is being done. If, when the selection event fires, work is still being done you set a flag that indicates the work should be repeated. Setting 'repeat_work' to true 5000 times is not expensive.

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

#3


I get the impression that you're trying to solve a problem through brute force. I would suggest trying a different event:

我得到的印象是你试图通过蛮力来解决问题。我建议尝试一个不同的事件:

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

I would suggest avoiding creating threads if at all possible, since they have overheaad. I couldn't see from your example where there's any need for parallelism.

我建议尽可能避免创建线程,因为它们已经超过了。我无法从你的例子中看到需要并行性的地方。

#4


First, while you are properly synchronizing access to PaintQueue, I feel it was more by chance in this situation as opposed to design. If you have other code accessing PaintQueue on other threads, then you have a problem.

首先,当您正确地同步对PaintQueue的访问时,我觉得在这种情况下,与设计相比,它更偶然。如果您有其他代码在其他线程*问PaintQueue,那么您就遇到了问题。

Second, this code makes no sense. You are spooling up a new thread, incrementing the value on that thread, and then waiting for 1/10th of a second. The thing is, the code that kicks off the thread is waiting on that thread to complete. Because of this, you are just waiting in the UI thread for nothing.

其次,这段代码毫无意义。您正在调整一个新线程,增加该线程上的值,然后等待1/10秒。问题是,启动线程的代码正在等待该线程完成。因此,您只是在UI线程中等待。

Even if you queue the SelectedIndexChange events, you aren't going to be able to prevent your app from hanging. The SelectedIndexChange event is going to fire every time that you select an item, and if the user selects 5000 items, then you need to process all 5000 events. You could give them a window (process every n seconds or whatever) but that's rather arbitrary and you put the user on a timer, which is bad.

即使您对SelectedIndexChange事件进行排队,也无法阻止您的应用挂起。每次选择项目时都会触发SelectedIndexChange事件,如果用户选择了5000个项目,则需要处理所有5000个事件。你可以给他们一个窗口(每n秒或其他任何处理),但这是相当随意的,你把用户放在计时器上,这很糟糕。

What you should do is not tie the operation to the SelectedIndexChanged event. Rather, have the user select the items and then have them perform some other action (click a button, for example) which will work on the selected items.

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

Your app will still hang though if you have to process a number of items for a lengthy period of time on the UI thread, but at least selecting the items won't hang.

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

#5


You don't really achieve any kind of concurrency by starting a new thread and then immediately Joining it. The only "effect" of the code above is that you method is run by another thread.

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

Additionally, if you want to use a background thread and safe the rather expensive cost of newing a thread, you should employ the thread pool.

另外,如果你想使用后台线程并且安全地花费相当昂贵的新线程,你应该使用线程池。

#1


I remember solving this issue before:

我记得之前解决过这个问题:

A better way perhaps for you would be to put a minimal delay in your ItemSelectionChange Handler. Say -- 50ms. Use a timer, Once the selection changes, restart the timer. If the selection changed more than once within the delay period, then the original is ignored, but after the delay has expired, the logic is executed.

对您而言,更好的方法是在ItemSelectionChange处理程序中放置最小延迟。说 - 50ms。使用计时器,选择更改后,重新启动计时器。如果选择在延迟时间内改变了不止一次,则原始被忽略,但在延迟期满后,执行逻辑。

Like this:

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


A possible solution is to delay the work, so you know whether or not more events have fired. This assumes the order of the selections is not important; all that matters is the current state.

一种可能的解决方案是延迟工作,因此您知道是否有更多事件被触发。这假设选择的顺序并不重要;重要的是当前的状态。

Instead of doing the work as soon as the event fires, set up a timer to do it a couple milliseconds after the event fires. If the timer is already running, do nothing. In this way the user should perceive no difference, but the actions will not hang.

事件触发后,不要在事件触发后立即执行工作,而是在事件触发后的几毫秒内设置一个计时器。如果计时器已在运行,则不执行任何操作。通过这种方式,用户应该感知没有区别,但操作不会挂起。

You could also do the work on another thread, but have a flag to indicate work is being done. If, when the selection event fires, work is still being done you set a flag that indicates the work should be repeated. Setting 'repeat_work' to true 5000 times is not expensive.

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

#3


I get the impression that you're trying to solve a problem through brute force. I would suggest trying a different event:

我得到的印象是你试图通过蛮力来解决问题。我建议尝试一个不同的事件:

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

I would suggest avoiding creating threads if at all possible, since they have overheaad. I couldn't see from your example where there's any need for parallelism.

我建议尽可能避免创建线程,因为它们已经超过了。我无法从你的例子中看到需要并行性的地方。

#4


First, while you are properly synchronizing access to PaintQueue, I feel it was more by chance in this situation as opposed to design. If you have other code accessing PaintQueue on other threads, then you have a problem.

首先,当您正确地同步对PaintQueue的访问时,我觉得在这种情况下,与设计相比,它更偶然。如果您有其他代码在其他线程*问PaintQueue,那么您就遇到了问题。

Second, this code makes no sense. You are spooling up a new thread, incrementing the value on that thread, and then waiting for 1/10th of a second. The thing is, the code that kicks off the thread is waiting on that thread to complete. Because of this, you are just waiting in the UI thread for nothing.

其次,这段代码毫无意义。您正在调整一个新线程,增加该线程上的值,然后等待1/10秒。问题是,启动线程的代码正在等待该线程完成。因此,您只是在UI线程中等待。

Even if you queue the SelectedIndexChange events, you aren't going to be able to prevent your app from hanging. The SelectedIndexChange event is going to fire every time that you select an item, and if the user selects 5000 items, then you need to process all 5000 events. You could give them a window (process every n seconds or whatever) but that's rather arbitrary and you put the user on a timer, which is bad.

即使您对SelectedIndexChange事件进行排队,也无法阻止您的应用挂起。每次选择项目时都会触发SelectedIndexChange事件,如果用户选择了5000个项目,则需要处理所有5000个事件。你可以给他们一个窗口(每n秒或其他任何处理),但这是相当随意的,你把用户放在计时器上,这很糟糕。

What you should do is not tie the operation to the SelectedIndexChanged event. Rather, have the user select the items and then have them perform some other action (click a button, for example) which will work on the selected items.

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

Your app will still hang though if you have to process a number of items for a lengthy period of time on the UI thread, but at least selecting the items won't hang.

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

#5


You don't really achieve any kind of concurrency by starting a new thread and then immediately Joining it. The only "effect" of the code above is that you method is run by another thread.

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

Additionally, if you want to use a background thread and safe the rather expensive cost of newing a thread, you should employ the thread pool.

另外,如果你想使用后台线程并且安全地花费相当昂贵的新线程,你应该使用线程池。