Thread: Passing data between a thread and a form

  1. #1
    Registered User
    Join Date
    Jun 2003
    Posts
    129

    Passing data between a thread and a form

    Just wondering how to go about this. I have a form with a status strip, amongst other things. I also have a background thread chugging along nicely. At certain times the thread wants to change the text of the status strip located on the form. What would be the best way of achieving this? I'm stumped basically.
    He who asks is a fool for five minutes, but he who does not ask remains a fool forever.

    The fool wonders, the wise man asks. - Benjamin Disraeli

    There are no foolish questions and no man becomes a fool until he has stopped asking questions. Charles Steinmetz

  2. #2
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675

  3. #3
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,820
    You will need to implement a delegate. Then in your thread Invoke that delegate.

    Here is a quick example. This is using a progress bar but the concept is the same.

    Code:
        public partial class Form1 : Form
        {
            /// <summary>
            /// Delegate function to use for progess bar update(This would be what method you would want to call)
            /// </summary>
            /// <param name="progress">The current progress of updates</param>
            private delegate void ProgessUpdateDelegate(int progress);
    
            /// <summary>
            /// The thread for the progress update.
            /// </summary>
            private Thread mProgressThread;
    
            /// <summary>
            /// The delegated method for updating progess
            /// </summary>
            private ProgessUpdateDelegate mProgessDelegate;
    
            public Form1()
            {
                InitializeComponent();
    
                //Create the delegate for the progress update.
                this.mProgessDelegate = new ProgessUpdateDelegate(this.UpdateProgressBar);
            }
    
            /// <summary>
            /// This is the function you would be updating the text.
            /// </summary>        
            private void UpdateProgressBar(int curentProgress)
            {
                //Update the progress value.
                this.progressBar.Value = curentProgress;
            }
    
            private void btnStartThread_Click(object sender, EventArgs e)
            {
                //
                //Create the thread and start it up.
                //
                mProgressThread = new Thread(new ThreadStart(this.StartFunctionWithProgress));
                mProgressThread.Start();
            }
            
            /// <summary>
            /// This would be the function where you are threading.
            /// </summary>
            private void StartFunctionWithProgress()
            {
                //
                //Function that involves proccessing. Simulating with thread sleeps.
                //
    
                //
                //Note: This is where you callback to update the UI.
                //
                Thread.Sleep(100);            
                this.Invoke(this.mProgessDelegate, 20);
    
                Thread.Sleep(100);
                this.Invoke(this.mProgessDelegate, 50);
    
                Thread.Sleep(100);
                this.Invoke(this.mProgessDelegate, 100);
            }
        }
    Woop?

  4. #4
    Registered User
    Join Date
    Aug 2008
    Posts
    188
    i'd recommend using BeginInvoke over Invoke. sooner or later you'll run into a deadlock if u use the synchronous version.

  5. #5
    Registered User
    Join Date
    Jun 2003
    Posts
    129
    Thanks guys, two great examples.
    He who asks is a fool for five minutes, but he who does not ask remains a fool forever.

    The fool wonders, the wise man asks. - Benjamin Disraeli

    There are no foolish questions and no man becomes a fool until he has stopped asking questions. Charles Steinmetz

  6. #6
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Quote Originally Posted by bling View Post
    i'd recommend using BeginInvoke over Invoke. sooner or later you'll run into a deadlock if u use the synchronous version.
    Well, while it's a good idea for several reasons to use BeginInvoke() over Invoke() when possible, but in my opinion, deadlocks shouldn't be a concern because the main thread shouldn't be blocking waiting for other threads. I try to meticulously avoid allowing the thread controlling the UI to block on any other thread, and if blocking is absolutely unavoidable, it must have a timeout.

    By the way, Dan, this is a common way you'll see this done. It's in fact my code that does the exact thing you're trying to do:

    Code:
            private delegate void UpdateStatusHandler(string msg);
            public void UpdateStatus(string str)
            {
                if (InvokeRequired)
                    BeginInvoke(new UpdateStatusHandler(UpdateStatus), new object[] { str });
                else
                {
                    toolStripStatusLabel1.Text = str;
                }
            }
    This allows you to call UpdateStatus() in ANY thread and have correct behavior. In a worker thread, it uses BeginInvoke() to run it on the main thread. In the main thread, it just sets the string directly.

    Use Invoke() instead of BeginInvoke() if you want to wait for the function to complete before the worker thread continues.
    Last edited by Cat; 11-24-2008 at 06:17 PM.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  7. #7
    Sweet
    Join Date
    Aug 2002
    Location
    Tucson, Arizona
    Posts
    1,820
    Also as a side note. When dealing with params you don't need to create the array yourself. This is handled by the compiler so you can just pass it the arguments you want from Invoke or BeginInvoke.

    Example updated the StartFunctionWithProgess
    Code:
        public partial class Form1 : Form
        {
            /// <summary>
            /// Delegate function to use for progess bar update(This would be what method you would want to call)
            /// </summary>
            /// <param name="progress">The current progress of updates</param>
            private delegate void ProgessUpdateDelegate(int progress, string someValue);
    
            /// <summary>
            /// The thread for the progress update.
            /// </summary>
            private Thread mProgressThread;
    
            /// <summary>
            /// The delegated method for updating progess
            /// </summary>
            private ProgessUpdateDelegate mProgessDelegate;
    
            public Form1()
            {
                InitializeComponent();
    
                //Create the delegate for the progress update.
                this.mProgessDelegate = new ProgessUpdateDelegate(this.UpdateProgressBar);
            }
    
            /// <summary>
            /// This is the function you would be updating the text.
            /// </summary>        
            private void UpdateProgressBar(int curentProgress, string someValue)
            {
                //Update the progress value.
                this.progressBar.Value = curentProgress;
    
                MessageBox.Show(someValue);
            }
    
            private void btnStartThread_Click(object sender, EventArgs e)
            {
                //
                //Create the thread and start it up.
                //
                mProgressThread = new Thread(new ThreadStart(this.StartFunctionWithProgress));
                mProgressThread.Start();
            }
            
            /// <summary>
            /// This would be the function where you are threading.
            /// </summary>
            private void StartFunctionWithProgress()
            {
                //
                //Function that involves proccessing. Simulating with thread sleeps.
                //
    
                //
                //Note: This is where you callback to update the UI.
                //
                Thread.Sleep(100);            
                this.Invoke(this.mProgessDelegate, 20, "Woop");
    
                Thread.Sleep(100);
                this.Invoke(this.mProgessDelegate, 50, "Coolness");
    
                Thread.Sleep(100);
                this.Invoke(this.mProgessDelegate, 100, "Dude");
            }
        }
    Woop?

  8. #8
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    I suggest using a System.ComponentModel.BackgroundWorker if you want a working thread in the background.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  9. #9
    Registered User
    Join Date
    Aug 2008
    Posts
    188
    Quote Originally Posted by Cat View Post
    Well, while it's a good idea for several reasons to use BeginInvoke() over Invoke() when possible, but in my opinion, deadlocks shouldn't be a concern because the main thread shouldn't be blocking waiting for other threads. I try to meticulously avoid allowing the thread controlling the UI to block on any other thread, and if blocking is absolutely unavoidable, it must have a timeout.
    in my experience, the larger the application gets, the more inevitable you will need to lock an object on the GUI thread.

    check out this article:
    http://kristofverbiest.blogspot.com/...gininvoke.html

  10. #10
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Quote Originally Posted by bling View Post
    in my experience, the larger the application gets, the more inevitable you will need to lock an object on the GUI thread.

    check out this article:
    http://kristofverbiest.blogspot.com/...gininvoke.html
    I guess it depends on programming style. In general, when possible I try to follow these criteria:

    * The main thread cannot block. Any code that absolutely needs to block gets moved out of the main thread.

    * A worker thread may block waiting on the main thread.

    * A worker thread may not block waiting on another worker thread.

    You're right, though, in that they're difficult criteria to code for, and I'm not always 100% successful in following those rules. In general, though, I try to do the following:

    * Main form owns the shared data, and is allowed to access it without blocking.

    * Worker threads shall either do atomic operations (Interlocked.whatever) or they need to Invoke/BeginInvoke. Often, atomic operations are sufficient, as Interlocked has quite a few nice tools in it, especially with Exchange and CompareExchange.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  11. #11
    Registered User
    Join Date
    Aug 2008
    Posts
    188
    i don't understand why you would go through all that trouble to ensure thread safety when all you need to do is use BeginInvoke instead of Invoke (not to mention performance increases as mentioned in the article i linked)

    also, your criteria for the GUI thread not blocking (and owning) objects has a major pitfall. as you probably know, foreach is not thread-safe, so if at the moment you are displaying data of a collection with a foreach, your worker thread adds or removes data from the collection, it'll throw an exception.

    EDIT: i see that this isn't a problem if you use Invoke after you update the collection.
    Last edited by bling; 11-25-2008 at 04:34 PM.

  12. #12
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Quote Originally Posted by bling View Post
    i don't understand why you would go through all that trouble to ensure thread safety when all you need to do is use BeginInvoke instead of Invoke (not to mention performance increases as mentioned in the article i linked)
    Oh, I agree with using BeginInvoke whenever possible. Sometimes, though, Invoke is what you need -- you want the worker thread to block and wait for the result of whatever operation you were doing with Invoke.

    also, your criteria for the GUI thread not blocking (and owning) objects has a major pitfall. as you probably know, foreach is not thread-safe, so if at the moment you are displaying data of a collection with a foreach, your worker thread adds or removes data from the collection, it'll throw an exception.
    You don't let the worker thread directly affect the collection; you force it to Invoke/BeginInvoke, and the main thread then alters the collection as requested by the worker.

    Also, if you really need to use locks on objects, you won't get any Invoke-related deadlocks as long as:

    1. The main thread doesn't block on workers except waiting for a lock, (e.g. it won't block waiting for a thread to terminate or anything like that).
    2. Worker threads likewise cannot block on other workers except waiting for a lock.
    3. The worker in question releases all its locks before Invoke().

    I'm not saying Invoke() is better than BeginInvoke() in all or even most cases. Sometimes, though, it's the right tool for the job.
    Last edited by Cat; 11-25-2008 at 04:45 PM.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  13. #13
    Registered User
    Join Date
    Jun 2003
    Posts
    129
    Woah, I've created quite the discussion haven't I!

    The program is very simplistic really, the background worker thread is merely there because that is quite intensive and I do not want the UI to hang during its work. The program consists of a listview, a status bar, a menu control and two main classes. The worker thread just contains a loop that will sometimes want to change the status bar text just for some additional information.
    He who asks is a fool for five minutes, but he who does not ask remains a fool forever.

    The fool wonders, the wise man asks. - Benjamin Disraeli

    There are no foolish questions and no man becomes a fool until he has stopped asking questions. Charles Steinmetz

  14. #14
    Registered User
    Join Date
    Aug 2008
    Posts
    188
    lol, i think we can agree to disagree :P

    actually, you steered me into diving deeper into the code. if you check the call stack of the invoked method it's the same for both the Invoke and BeginInvoke variants, which means the only difference is the Invoke is calling EndInvoke for you.
    Last edited by bling; 11-25-2008 at 05:20 PM.

  15. #15
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Guess we can agree on that much

    Anyhow, though, one final clarification of mine -- the "main thread never blocks" isn't just to avoid deadlocks. Its main point relates to one of my two major pet peeves: when the GUI becomes nonresponsive. I don't let the GUI thread block because I don't want the GUI to hang, under any condition, even if I only expect it to do so briefly.

    (My second pet peeve consists of applications that don't handle Unicode inputs, especially in the case of Unicode filenames. If the file has a legal filename on my operating system, you will support it in your software.)
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

Popular pages Recent additions subscribe to a feed