views:

118

answers:

3

This question has probably been asked in various ways before, but here is what I want to do. I am going to have a Windows form with many tabs. Each tab will contain a grid object. For each tab/grid that is created by the user, I would like a spawn off a dedicated thread to populate the contents of that grid with constantly arriving information. Could anyone provide an example of how to do this safely?

Thanks.

+3  A: 

Inside the initialization for the tab (assuming WinForms until I see otherwise):

Thread newThread = new Thread(() =>
{
    // Get your data

    dataGridView1.Invoke(new Action(() => { /* add data to the grid here */ } );
});

newThread.Start();

That is obviously the most simple example. You could also spawn the threads using the ThreadPool (which is more commonly done in server side applications).

If you're using .NET 4.0 you also have the Task Parallel library which could help as well.

Justin Niessner
My concern with this approach would be the number of threads and the amount of work each has to do. The OP specifies "many" tabs -- too many tabs with too much work to do could result in significant thrashing that would actually hurt performance.ThreadPool would manage this and TPL would manage it even better.
Ragoczy
@Ragoczy - That's exactly why I mention that this was meant to be the most simple example and that the OP should probably check out the ThreadPool and TPL (if it's available to him).
Justin Niessner
Thanks for your comments. I doubt any user would have more than 3-5 tabs open at one time. That would be the most.
Randy Minder
@Randy. If it's possible for the user to open over 200 tabs, then some probably will.
Jon Hanna
A: 

You should have an array of threads, to be able to control them

List<Thread> tabs = new List<Thread>();

...

To add a new one, would be like:

tabs.Add( new Thread( new ThreadStart( TabRefreshHandler ) );
//Now starting:
tabs[tabs.Count - 1].Start();

And finally, in the TabRefreshHandler you should check which is the calling thread number and you'll know which is the tab that should be refreshed!

David Conde
How do you plan on controlling the threads via the list (a curiosity question)?
Justin Niessner
Its very easy, if you want to stop Tab3 from updating you should stop Thread in the 3rd slot of the list, since they should be added in the same time, the idea is to have a relationship between the thread number in the list and the tab number. You could also use a HashTable to store the tab's name. I think this way will give you more control in the updates
David Conde
@dconde - The reason I was asking was that there are better ways of signaling between threads than simply using the original thread object to stop execution.
Justin Niessner
+1  A: 

There are two basic approaches you can use. Choose the one that makes the most sense in your situation. Often times there is no right or wrong choice. They can both work equally well in many situations. Each has its own advantages and disadvantages. Oddly the community seems to overlook the pull method too often. I am not sure why that is really. I recently stumbled upon this question in which everyone recommeded the push approach despite it being the perfect situation for the pull method (there was one poor soul who did go against the herd and got downvoted and eventually deleted his answer leaving only me as the lone dissenter).

Push Method

Have the worker thread push the data to the form. You will need to use the ISynchronizeInvoke.Invoke method to accomplish this. The advantage here is that as each data item arrives it will immediately be added to the grid. The disadvantage is that you have to use an expensive marshaling operation and the UI could bog down if the worker thread acquires the data too fast.

void WorkerThread()
{
  while (true)
  {
    object data = GetNewData();
    yourForm.Invoke(
      (Action)(() =>
      {
        // Add data to your grid here.
      }));
  }
}

Pull Method

Have the UI thread pull the data from the worker thread. You will have the worker thread enqueue new data items into a shared queue and the UI thread will dequeue the items periodically. The advantage here is that you can throttle the amount of work each thread is performing independently. The queue is your buffer that will shrink and grow as CPU usage ebbs and flows. It also decouples the logic of the worker thread from the UI thread. The disadvantage is that if your UI thread does not poll fast enough or keep up the worker thread could overrun the queue. And, of course, the data items would not appear in real-time on your grid. However, if you set the System.Windows.Forms.Timer interval short enough that might be not be an issue for you.

private Queue<object> m_Data = new Queue<object>();

private void YourTimer_Tick(object sender, EventArgs args)
{
  lock (m_Data)
  {
    while (m_Data.Count > 0)
    {
      object data = m_Data.Dequeue();
      // Add data to your grid here.
    }
  }
}

void WorkerThread()
{
  while (true)
  {
    object data = GetNewData();
    lock (m_Data)
    {
      m_Data.Enqueue(data);
    }
  }
}
Brian Gideon
Nice answer. In my experience the pull method is almost always the best approach for production-quality software.
Dan Tao
@Dan: Yeah, its amazing how often it is overlooked. My theory is that it is the herd effect. Once the idea gets planted and develops a critical mass it takes off and everything else gets ignored and written off without due dilegence.
Brian Gideon