C Board  

Go Back   C Board > General Programming Boards > C# Programming

Reply
 
LinkBack Thread Tools Display Modes
Old 11-24-2008, 12:35 PM   #1
Registered User
 
Join Date: Jun 2003
Posts: 90
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
DanFraser is offline   Reply With Quote
Old 11-24-2008, 01:14 PM   #2
and the Hat of Ass
 
Join Date: Dec 2007
Posts: 810
This should help, Dan.
rags_to_riches is offline   Reply With Quote
Old 11-24-2008, 01:30 PM   #3
Sweet
 
Join Date: Aug 2002
Location: Tucson, Arizona
Posts: 1,697
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?
prog-bman is offline   Reply With Quote
Old 11-24-2008, 02:06 PM   #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.
bling is offline   Reply With Quote
Old 11-24-2008, 02:57 PM   #5
Registered User
 
Join Date: Jun 2003
Posts: 90
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
DanFraser is offline   Reply With Quote
Old 11-24-2008, 06:15 PM   #6
Cat
Registered User
 
Join Date: May 2003
Posts: 1,199
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.
__________________
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.

Last edited by Cat; 11-24-2008 at 06:17 PM.
Cat is offline   Reply With Quote
Old 11-24-2008, 06:27 PM   #7
Sweet
 
Join Date: Aug 2002
Location: Tucson, Arizona
Posts: 1,697
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?
prog-bman is offline   Reply With Quote
Old 11-25-2008, 01:04 AM   #8
Confused
 
Magos's Avatar
 
Join Date: Sep 2001
Location: Sweden
Posts: 3,125
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.
Magos is offline   Reply With Quote
Old 11-25-2008, 11:57 AM   #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
bling is offline   Reply With Quote
Old 11-25-2008, 04:18 PM   #10
Cat
Registered User
 
Join Date: May 2003
Posts: 1,199
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.
Cat is offline   Reply With Quote
Old 11-25-2008, 04:24 PM   #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.
bling is offline   Reply With Quote
Old 11-25-2008, 04:43 PM   #12
Cat
Registered User
 
Join Date: May 2003
Posts: 1,199
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.

Quote:
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.
__________________
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.

Last edited by Cat; 11-25-2008 at 04:45 PM.
Cat is offline   Reply With Quote
Old 11-25-2008, 04:51 PM   #13
Registered User
 
Join Date: Jun 2003
Posts: 90
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
DanFraser is offline   Reply With Quote
Old 11-25-2008, 05:14 PM   #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.
bling is offline   Reply With Quote
Old 11-28-2008, 02:52 AM   #15
Cat
Registered User
 
Join Date: May 2003
Posts: 1,199
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.
Cat is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Forum Jump


All times are GMT -6. The time now is 06:13 PM.


Powered by vBulletin® Version 3.8.1
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.3.2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22