views:

891

answers:

5
A: 

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.

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.

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.

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.

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.

casperOne
+1  A: 

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.

Strilanc
+2  A: 

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.

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");
}
}
Matt Brunell
This is basically what I was doing (not in my sample hack above, but in my real code.) I was spawning a thread and letting it sleep. The problem is one of the controls (that I have no control over) requires that this data be in the same thread as itself.
Jerry
Use a dispatching timer instead?
FryGuy
You can only modify UI elements from the thread that created them. This is a rule for windows forms. You can switch to the UI thread by calling 'Invoke', but I think the timer class handles this automatically.
Matt Brunell
HOLY CRAP! It does.... I did some testing, and it worked perfectly! I thought Timer put it in a new thread, but the picky control that I can't touch seems to be very happy with this.
Jerry
The winforms timer fires events on the UI thread. If you're using WPF (I assumed you were for some reason), then you need to use the DispatchingTimer (fires on UI thread), as opposed to the System.Threading.Timer (fires on threadpool thread).
FryGuy
+1  A: 

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.

Michael Meadows
I am using SelectedIndexChanged. I *SHOULD* be using this event instead. Life just became MUCH easier. Thank you for the input!!
Jerry
A: 

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.

Brian Rasmussen
Yes.. I posted the code as "here's what I'm trying to do... sort of." I know that this doesn't work because Join blocks the current thread which completely negates the purpose of having a thread. How do I get around that is my question.
Jerry
Sorry, Jerry I missed that. I'll have another look then.
Brian Rasmussen
Could you tell us a little bit about why you need the code to run on a specific thread? If it is because you have to access GUI controls, you can marshal your call to the GUI thread when needed. Otherwise please elaborate.
Brian Rasmussen