Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > Visual Basic > VB.NET 1.0 > Pro VB.NET 2002/2003
Password Reminder
Register
Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
Pro VB.NET 2002/2003 For advanced Visual Basic coders working .NET version 2002/2003. Beginning-level questions will be redirected to other forums, including Beginning VB.NET.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the Pro VB.NET 2002/2003 section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developers’ questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Search this Thread Display Modes
  #1 (permalink)  
Old April 12th, 2007, 09:27 AM
Registered User
 
Join Date: Apr 2007
Location: , , .
Posts: 9
Thanks: 0
Thanked 0 Times in 0 Posts
Default Multi-Threading Issue

Let me be very general.

I'm creating an application that needs to run a process continuously until the user kills it. So run the continuous process on a separate thread until the user clicks a 'Kill' button right? The problem is that this process needs to do a lot of updating to controls on the main UI. Scouring the internet for information has led me to believe that it is bad practice to update UI controls created on the main thread from a separate thread.

How can I give the user a way to kill a continual process that constanly updates controls on the UI? Apparently 'background worker in Studio 2005 handles this easily, but I am limited to 2003.

Thanks for any help.

Reply With Quote
  #2 (permalink)  
Old April 12th, 2007, 10:02 AM
planoie's Avatar
Friend of Wrox
Points: 16,481, Level: 55
Points: 16,481, Level: 55 Points: 16,481, Level: 55 Points: 16,481, Level: 55
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Aug 2003
Location: Clifton Park, New York, USA.
Posts: 5,407
Thanks: 0
Thanked 16 Times in 16 Posts
Default

It's not just bad practice, but it doesn't work. The runtime will throw an error when you try to update a control created by another thread.

It's been a while since I've done this, but it involves using the "BeginInvoke" method of the form class you want to update. Basically, BeginInvoke() takes a delegate to the method you want to invoke (this would be a method on the form that will update the controls). This causes the class on which BeginInvoke is called (the form) to perform the actual invocation of the method, thus crossing the thread boundary back to the one that owns the controls.

-Peter
Reply With Quote
  #3 (permalink)  
Old April 12th, 2007, 11:40 AM
Registered User
 
Join Date: Apr 2007
Location: , , .
Posts: 9
Thanks: 0
Thanked 0 Times in 0 Posts
Default

I'm actually pretty unclear on how this works. Can anyone think of a simple example or post a link to simple example to understand the gist of marshalling?

Reply With Quote
  #4 (permalink)  
Old April 12th, 2007, 01:42 PM
planoie's Avatar
Friend of Wrox
Points: 16,481, Level: 55
Points: 16,481, Level: 55 Points: 16,481, Level: 55 Points: 16,481, Level: 55
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Aug 2003
Location: Clifton Park, New York, USA.
Posts: 5,407
Thanks: 0
Thanked 16 Times in 16 Posts
Default

Maybe I can explain with an analogy:

Imagine your program is a lunch Diner. The key players (and what they relate to in your program):

Cook (the form running in the main program thread): makes the food (updates the UI)
Waiter (background worker thread): takes customer's orders (runs your continuous task)

The window to the kitchen is the thread boundary.

Cook spawns off Waiters. The waiters go around doing their tasks in the dining room. When a waiter gets an order, he write it down (prepares data for UI update) and goes to the window. The waiter calls to Cook "Hey Cook! Make this order, here it is." and hands the order ticket thru the window (crossing the boundary).

In your code, the Cook is the main form (running in the parent thread) so your code looks like this:

this.BeginInvoke(new delegate(StartCooking), new object[] {Order});

To map the Diner scenario to the code:

Hey Cook! -> this
Make -> .BeginInvoke( )
this order, -> new delegate(StartCooking)
here it is. -> new object[] {Order}

BeginInvoke will call the "StartCooking" method and pass it the "Order". The ACTUAL call to "StartCooking" is made by an object in the parent thread, versus the object in the child thread that called "BeginInvoke".

In your code, "StartCooking" will be "UpdateUI". Your worker process will invoke it with the necessary information to update the UI controls. However, the call will actually be made from the main thread which permits control updates.

I hope this helps explain how it works. I have a program that does this, but it would take a while to extract all the non-important code from the relevant stuff to show you. However, I got it from an article somewhere, so you should be able to find something similar.

-Peter
Reply With Quote
  #5 (permalink)  
Old April 12th, 2007, 02:28 PM
Registered User
 
Join Date: Apr 2007
Location: , , .
Posts: 9
Thanks: 0
Thanked 0 Times in 0 Posts
Default

I appreciate the analogy to an everyday situation. The idea behind threading is brought out plainly in your diner scenario. At the risk of making myself look too much like a beginner, I have to ask for a bit more help.

The concepts being solid, it's the syntax that gets me. Especially when it comes to delegates. Consider the following elementary problem:

1. A form with a textbox (displaying count) and two buttons (start, kill)
2. If the user clicks on the start button, an infinite loop is commenced say,

dim i as integer
i=1
while i>0
i=i+1
end while

3. Obviously, the user has a long wait in front of them. But let's say that while the programmer wasn't quick-witted enough to avoid the infinite loop, he was code-savvy enough to put the incrementing process on a separate thread. Now we can say that the start button event handler is used to initiate the creation of a secondary thread where the above code will now be processed.

4. The kill button affords the user the opportunity to .abort the loop. Problem solved.

5. But suppose while the variable is incrementing on it's separate thread, we want the counter to update in the textbox (perhaps using the thread sleep command for 3 second pauses)? This necessitates invoking the original thread, which is where I lose track of the syntax.

I believe that if someone can show me the proper syntax for this procedure, I can take it from there.

One more question - is the moving back and forth between threads a time consuming process?

Again thanks for your help.
Reply With Quote
  #6 (permalink)  
Old April 12th, 2007, 07:50 PM
planoie's Avatar
Friend of Wrox
Points: 16,481, Level: 55
Points: 16,481, Level: 55 Points: 16,481, Level: 55 Points: 16,481, Level: 55
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Aug 2003
Location: Clifton Park, New York, USA.
Posts: 5,407
Thanks: 0
Thanked 16 Times in 16 Posts
Default

For a beginner, you have a good grasp of what's going on.

I just found this article that explains in much greater detail the specifics of how all this works:

http://www.codeproject.com/csharp/begininvoke.asp

In your step # 5, you have the exact right idea. The "worker" thread has to tell the form thread to invoke the form control update method.

To refresh my own memory I whipped up a sample app. I'll post the relevant part of the code. Hopeful it will get you going in the right direction. Here is the important parts of a sample winform app:
Code:
    Private _objThread As Thread

    Private Sub cmdStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdStart.Click
        cmdStart.Enabled = False
        _objThread = New Thread(AddressOf ContinuousProcess)
        _objThread.Start()
        cmdStop.Enabled = True
    End Sub

    Private Sub cmdStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdStop.Click
        cmdStop.Enabled = False
        _objThread.Abort()
        _objThread = Nothing
        cmdStart.Enabled = True
    End Sub

    Private Sub ContinuousProcess()
        Dim i As Integer = 0
        While True
            i += 1
            Me.BeginInvoke(New CounterUpdateDelegate(AddressOf UpdateCounter1), New Object() {i})
            Me.BeginInvoke(New CounterUpdateDelegate(AddressOf UpdateCounter2), New Object() {i})
            Thread.Sleep(1000)
        End While
    End Sub

    Delegate Sub CounterUpdateDelegate(ByVal count As Integer)

    Private Sub UpdateCounter1(ByVal count As Integer)
        txtCount1.Text = count.ToString
    End Sub
    Private Sub UpdateCounter2(ByVal count As Integer)
        txtCount2.Text = count.ToString
    End Sub
Delegates can be tricky to understand at first, hope this clarifies it. Think of a delegate statement in the code as a definition of a method signature. In my example:

Delegate Sub CounterUpdateDelegate(ByVal count As Integer)

defines "CounterUpdateDelegate" as a method that will take an integer and return nothing. I can have many methods that conform to that signature definition. In the example above, I intentionally created two to illustrate this point.

When I want to use one of the methods that conform to my delegate type, I create an instance of the delegate type defined ("CounterUpdateDelegate") and put into it the address of the method I wish to actually use. This is what is known as a Call Back. In this case, the logic inside of BeginInvoke is going to "Call Back" to the method I handed it through the callback delegate.

-Peter
Reply With Quote
  #7 (permalink)  
Old April 14th, 2007, 01:08 PM
Registered User
 
Join Date: Apr 2007
Location: , , .
Posts: 9
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Peter,

Thanks a million for the basic code. This is the easy to understand item I've been looking everywhere for.

If you have some time, maybe you can answer a few more questions for me:

1. If my background thread needs to pass back say ten or twelve values, should I expect the processing time to get really bogged down?

2. Do I need to have a separate 'begininvoke' statement for each control that I need to update on the UI or is there a more wholistic approach?

3. When do I need to start worrying about synchroniztion and 'synclock'?

4. In the background thread, can you refer directly to values associated with a control, or do you have to pass these values to the thread? Put more simply - while we know you can't write from the background thread, can you at least read values in from the main thread? If not, how do you pass the values?

I apologize if I'm loading you up with questions, but to this point you've been my best resource. Thanks again for your help.




Reply With Quote
  #8 (permalink)  
Old April 15th, 2007, 07:19 PM
planoie's Avatar
Friend of Wrox
Points: 16,481, Level: 55
Points: 16,481, Level: 55 Points: 16,481, Level: 55 Points: 16,481, Level: 55
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Aug 2003
Location: Clifton Park, New York, USA.
Posts: 5,407
Thanks: 0
Thanked 16 Times in 16 Posts
Default

1. Because of the way they constructed the BeginInvoke method, you can pass it anything you wish. The second argument is simply an object array so you can create an array of values you need to pass to the method that will be called:

   Me.BeginInvoke(New CounterUpdateDelegate(AddressOf UpdateCounter1), New Object() {value1, value2, value3, ...})

Alternatively, and in many cases easier to deal with, create a class that will contain all the "arguments". This can be used in much the same way as .NET uses event args classes. Your class would have properties for all the values you need to pass to the other thread, and you pass an instance of the object as a single argument to the BeginInvoke call. Of course, by doing this you have to consider thread safety.

   Me.BeginInvoke(New CounterUpdateDelegate(AddressOf UpdateCounter1), New Object() {myFormUpdateArguments})


2. The method delegate called by the BeginInvoke call can update all controls as needed.

3. I'm not really sure.

4. There's no problem here. Yes, you can not update controls created by another thread, but there's nothing preventing you from READing values from another thread's controls.


One misconception that I'm still fighting is that different threads are in different scopes of a program. I'm not sure why I have had this idea in my head but I do. It's important to remember that several threads created by the same applications are all still within the scope of the same application. It's just that another *process* is handling the execution of some number of method calls. The point here is that secondary threads within a given program can see everything that the main thread sees (according to standard scope visibility of course). Granted, some rules need to be adhered to, such as simultaneous accessing of certain resources (like files) and restrictions on what can be updated (as we've been discussing) but otherwise, the code runs just the same as if you called it from the single main thread.

-Peter
Reply With Quote
  #9 (permalink)  
Old April 22nd, 2007, 01:50 PM
Registered User
 
Join Date: Apr 2007
Location: , , .
Posts: 9
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Peter,

New thoughts on the same application. The threading is working well for me now, except...

I would like the secondary thread to stop operating while the main thread has been invoked to update GUI controls. Ordinarily this would be done with the .join() method, correct? If I understand that method definition properly, the thread using the .join method halts until the target thread of the join method terminates. But in reality, the main thread doesn't terminate until the application terminates, right? So is it impossible to .join the gui thread? Or more simply, how can I halt the secondary thread until the main thread has finished updating the GUI. Also, sample syntax if you can.

As always, your help (and anyone else's, of course) would be invaluable.

Jason

Reply With Quote
  #10 (permalink)  
Old April 23rd, 2007, 06:52 AM
planoie's Avatar
Friend of Wrox
Points: 16,481, Level: 55
Points: 16,481, Level: 55 Points: 16,481, Level: 55 Points: 16,481, Level: 55
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Aug 2003
Location: Clifton Park, New York, USA.
Posts: 5,407
Thanks: 0
Thanked 16 Times in 16 Posts
Default

I am not an expert on threading. I've just used it enough to get myself into trouble ;).

It would appear your description is correct. From the MSDN docs for Thread.Join Method () :
"Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping."

Yes, the main (UI) thread never exits until you close/exit the application.

Are you looking to *suspend* the worker thread while the GUI updates? Or do you actually want to stop it?

If you want to suspend it, I'd ask the question: Why would you want to do that? The whole point of a worker thread is so you have a thread doing the work as quickly as possible in the background while still allowing the main (UI) thread to update and access messages (i.e. button click, etc). Suspending the worker thread for UI updates will just slow things down. There may be better ways to do this, such as not ALWAYS updating the UI. Instead you update it on some time interval.

-Peter
Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
error handling while multi threading minotaur01 General .NET 0 August 9th, 2007 05:55 PM
Multi-threading issue Bill Crawley Pro Visual Basic 2005 0 March 21st, 2007 06:19 AM
multi threading rashid.shaban@gmail.com BOOK: ASP.NET Website Programming Problem-Design-Solution 1 February 26th, 2007 11:48 PM
Multi threading, web service requests sheel VS.NET 2002/2003 2 January 12th, 2007 04:39 AM
ASP.NET Multi-Threading, is there such a thing? flyin General .NET 1 April 23rd, 2004 03:05 PM



All times are GMT -4. The time now is 11:54 AM.


Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.