3

I have a multi-threaded application where each thread has the ability to log pending GUI updates by utilizing a shared (and thread safe) BlockingCollection that contains guiUpdateObjects.

Each guiUpdateObject contains information on which control to update and the content to be displayed. I'd like to have my Form consume any items that exist on that BlockingCollection and update the various controls accordingly.

This set up worked well for me previously in the Java version of this application where after spawning threads and doing initialization tasks, the main thread checked for items on a timed loop.

I suppose I could do the same in C# with a timer event but given all the native event handling in Windows Forms, I'm wondering if there's a better way. Perhaps, with a custom event? Would appreciate any criticisms on my current approach and/or suggestions on how best to implement this in C#.

Update based on Hans suggestion:

After reading up on Control.BeginInvoke(), I understand that this will allow me to PostMessage to the application message queue. This is similar to my utilization of BlockingCollection in that it's thread safe and immediately returns without waiting for the message to be processed.

However, utilizing BeginInvoke requires using delegates and after reading up on them, I'm still a bit confused on proper implementation. Do I declare the delegate in my Form class or in the 'worker' class that will be generating content for gui updates? If I declare in my Form class, the method won't be visible to my worker class unless I reference the Form object. Alternatively, if I declare in the worker class, how will the Form class make the association between the delegate method and the actual method? Here's some code highlighting my question:

public partial class GUI : Form
{

    public GUI()
    {
        InitializeComponent();

        Thread workerThread = new Thread(new ThreadStart(new Worker().DoWork));
        workerThread.Start();
    }

    private delegate void AppendSysLogDelegate(string logEntry);
    private void AppendSysLog(string logEntry)
    {
        if (this.InvokeRequired)
        {
            this.BeginInvoke(new AppendSysLogDelegate(AppendSysLog), new object[] { logEntry });
            return;
        }
        systemLogger.AppendText(logEntry);
    }
}

public class Worker
{
    public void DoWork()
    {
    //what goes here to call AppendSysLog("Test log entry");
    }
}
cstack22
  • 31
  • 2
  • IMO, there is no such thing as a "C# form". – Uwe Keim Jan 18 '14 at 17:55
  • 4
    You re-invented the invoke queue that Control.BeginInvoke() maintains. Yes, there is an event behind it that ensures that this queue gets emptied, it is in internal. Just use BeginInvoke(). Read more about it in any introductory article or book about Winforms programming. – Hans Passant Jan 18 '14 at 17:55
  • @HansPassant thanks - there's a pretty good tutorial on BeginInvoke here: http://www.codeproject.com/Articles/10311/What-s-up-with-BeginInvoke. I updated my original question with some follow ups on BeginInvoke and delegates. – cstack22 Jan 18 '14 at 19:34
  • Also, would it hurt anything to make the AppendSysLog method static so that I wouldn't have to pass the GUI object around to every class that uses it in order to make it visible? – cstack22 Jan 19 '14 at 18:18
  • The easiest way to think of a delegate is to just to imagine it referring to the method signature. Where you see a delegate as a parameter you can use a lambda (anonymous method) in it's place or just any other named method that matches the delegate's method signature. – JNYRanger Jan 21 '14 at 02:08

1 Answers1

0

Check this question and make a note of the excellent answer:

Is there anything like asynchronous BlockingCollection<T>?

Now that you have been armed with an equivalent of blockingCollection.TakeAsync(), you can await its result on a UI thread, for example:

async void btnTest_Click(object s, EventArgs e)
{
   // don't forget to add exception handling
   while(true)
   {
        var result = await blockingCollection.TakeAsync();
        this.textBox.AppendText(result.ToString());
   }
}
Community
  • 1
  • 1
avo
  • 10,101
  • 13
  • 53
  • 81