An explanation of threading in VB.NET. This new exciting feature lets you FINALY execute code in an async fashion.
Terms of Agreement:
By using this article, you agree to the following terms...
You may use
this article in your own programs (and may compile it into a program and distribute it in compiled format for languages that allow it) freely and with no charge.
You MAY NOT redistribute this article (for example to a web site) without written permission from the original author. Failure to do so is a violation of copyright laws.
You may link to this article from another website, but ONLY if it is not wrapped in a frame.
You will abide by any additional copyright restrictions which the author may have placed in the article or article's description.
In this article I will try and give you an example of one of
the most exciting new feature of VB.NET… Free threading! Take a look the
following VB6 code:
Dim myTest as Ctest
Public Function TestFunction(Id as Integer) as Integer
Dim Ret as Integer
Set myTest = NewCtest
Ret =
MyTest.DoSomething(Id)
TestFunction = Ret
Set myTest = Nothing
End Function
What happens here is very simple. We have a class (Ctest)
which contains a method called: DoSomething. The DoSomething method takes one
argument (Id) and returns an integer. The above piece of code wraps all this
neatly in a function. I am sure you’ve done something like this a million times
before.
Now what happens when DoSomething is a method that runs for
a very long time? Let’s say it iterates through an ADO Recordset containing a
thousand records? Right! Your code is going to hit the line:
Ret =
MyTest.DoSomething(Id)
…And wait till the DoSomething method is done before your
code will continue with the line below it (the one that sets TestFunction to
the return value of DoSomething). While this is acceptable in most cases, for
instance if DoSomething was to contain the code that saves a long document and
the user has to wait anyway, it is very annoying behavior in a lot of other
cases. I have tried every trick in the book, but VB6 doesn’t have a neat way of
handling this situation. At some point your code will be waiting for other code
to finish. If you ever tried keeping the GUI responsive with DoEvents while
your application is off to do some expensive database operation, you will know
exactly what I mean.
VB.NET to the rescue! Enter stage left, FREE THREADING.
Wouldn’t it be great if you had a way to tell VB that you really don’t need to
wait but instead want to go on with whatever the next task of your code is? You
can!
The trick is in:
System.Threading.Thread
The system.threading namespace allows you to push methods of
a class or subs and functions of your regular code off to its own thread.
Create a form with a button on it called Button1. Then…
‘the following goes under the Inherits portion of your VB code
Dim Test as New Ctest
Dim ThreadTest as System.Threading.Thread(AddressOff
MyThreadTest)
‘this is a simple Sub:
Public Sub MyThreadTest
Test.DoSomeThing(Id)
End Sub
‘this is the button’s click event
Private
Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles Button1.Click
MsgBox("Beginning New Threads")
ThreadTest = New
System.Threading.Thread(AddressOf _ MyThreadTest)
ThreadTest.Start()
MsgBox(“Done”)
End
Sub
Do NOT run this code! IT WILL FAIL. There is a couple of
things we still need to do, but the above part shows us what is going on when
we get into multi-threading. So what the heck is going on??
The first line we simply set a reference (Dim) to our Ctest
class (remember? The one with the very loooooong running method.) In the next
line we simply tell VB to declare a new thread! That is really most of what
there is to it. By saying “Dim <somename> as new
System.Threading.Thread(AddressOf <somemethod>)” We have created a new
thread to call a method/sub on. The AddressOf operator might be a surprise to
you. What you are doing there is saying to VB “Hey when I start doing something
on that new thread I want you to be doing <insert name of method/sub>”.
Some people might now say that I am over simplifying this… I AM! But for now
that is all you need to know.
You can see that I pointed the thread to a Sub
(AddressOfMyThreadTest ‘MyThreadTest
is a Sub) and not directly to the class we are using. There is a very good
reason for that. If you want anything to run in its own thread you can’t pass
arguments, or receive return values. I know what you are thingking…. “Geez,
that’s not very useful!” But think about it for a second. You are telling VB to
start a new thread… A NEW THREAD! Not a new instance of a class, not another
copy of an ActiveX Server… A NEW THREAD! You completely disconnected the code
on that thread from the main application. The application you’re doing this in
doesn’t know anything about this new thread, and quite frankly, it doesn’t give
a rodent’s butt-cheek. That is why you we need to wrap that code into a sub
that our application DOES know about and that is exactly what I am doing this
piece of code:
Public Sub MyThreadTest
Test.DoSomeThing(Id)
End Sub
I set my new thread to be: AddressOf MyThreadTest. Even
though The Sub doesn’t take any arguments or returns anything, I can have the
call to the Ctest class inside of it take an argument (Id). Oh! This is why I
said not to run it yet previously. VB.NET will complain that it doesn’t know “Id”.
For testing purposes we can quickly fix this. Change the code of the Sub
MyThreadTest to read:
Public Sub MyThreadTest
Dim Id as Integer
Id = 10
Test.DoSomeThing(Id)
End Sub
Of course to run all this you better create a class in your
project called Ctest with a DoSomething Function that takes an Integer (Id) as
an argument, but you get the idea.
The last code in your project is the Click event of the
button I had you put on the form. The most important line of code inside it is:
ThreadTest.Start()
See what we are doing here is not calling the Sub MyThreadTest.
Instead we are saying: I declared a thread called ThreadTest, Start it! Because
we previously set the ThreadTest to be AddressOf MyThreadTest, VB will now run
the code inside that Sub in its own thread.
To get the full effect of this you should really add a class
called Ctest to your project and create a DoSomething function in it that does
something that takes very very very looooong. If you’ve done that, start the
application and click on the button. What you will see is the first message box
reading: Beginning New Threads. If you on OK, the code will jump into the Sub
MyThreadTest. But rather then waiting for execution of this Sub (and the call
to the Ctest method DoSomething inside it) it will almost immediately show the
next message box (“Done!”). And THAT is free threading in a nutshell… Cool or
what??
very nice article, are you going to write anything about delegates? since they look quite powerfull, and are related to threading. (If this comment was disrespectful, please report it.)
10/25/2002 7:41:00 AM:
Interesting article ;) (If this comment was disrespectful, please report it.)
11/17/2002 9:16:41 PM:
Nice article. Very helpful. Thanks (If this comment was disrespectful, please report it.)
12/1/2002 11:42:19 PM:
Thanks. Wonderful article. (If this comment was disrespectful, please report it.)
12/3/2002 7:34:57 AM:
Great article. My only question, how do you pass anything into the code you want to run? (If this comment was disrespectful, please report it.)
I like your "layman's" approach. Too many programmers can be too specific for fear of being criticized by their peers. This is a shame, because I learned more from your article than the many hours of reading the rigid details. Lot's of facts, but no substance.
Please keep up the good work! (If this comment was disrespectful, please report it.)
2/1/2003 4:38:25 PM:
gerçekten iyi hazýrlanmýþ tebrikler (If this comment was disrespectful, please report it.)
Great article.very informative while being simple at the same time.One question though, how does the thread call back the main application? Thanks (If this comment was disrespectful, please report it.)
Ah yes, thanks for your easy to understand article, but i have some questions. What if inside your thread you need to update some information on the form, how would you go about doing that?
And what if instead of calling class to do the threading, you do all your work in the sub MyThreadTest itself?
Also, when the work is done inside the thread, is the thread killed? What if you want to kill/stop a thread you started, how would you do that? (If this comment was disrespectful, please report it.)
Karim, of course you can do the work inside the sub itself and never use a class. However, as I stated in my article, subs that are pushed to their own thread can neither take arguments as input nor can they return anything. The omission of a return value can be overcome by using a global varibale. This is not a very neat solution. Especially since the the work performed by the sub is on its own thread and the main application has no idea when it finishes. (If this comment was disrespectful, please report it.)
It therefore would need to check the global var continuously to see if it contains a value (genereated by the free-threaded sub) already that it can use. This is far more overhead in your application then needed, since extra work is being performed (the check if the global var is filled yet). Furthermore, this method can only be used if you are really careful about cleaning up vars or you might end up having your main code use a value from the global var that was put there by a previous instance of the thread. (If this comment was disrespectful, please report it.)
This solution becomes unpractical if you ever need more then 1 thread using the same sub. You would need just as many global variables as you need threads, making your app scale very poorly. Instead you should raise an event that has your return value as a parameter. In order to have your main code "listen" for this event it NEEDS to be declared in a class. This also answers your first question, "how to change something on the form based on what happens in the thread?" (I reworded it a little). Again, if you raise an event, your form can "listen" for the event and change some of its properties based on the parameters of the event. Lasty, stopping a thread can simply be done by calling: ThreadTest.Abort (If this comment was disrespectful, please report it.)
the use of ThreadTest.Name is quite helpful to identify threads later (not by code, but by debug outputs, logfiles, eventlog, etc...) (If this comment was disrespectful, please report it.)
Thanks. **** (If this comment was disrespectful, please report it.)
7/27/2003 5:36:41 AM:
Thanks! (If this comment was disrespectful, please report it.)
9/5/2003 9:19:11 AM:
might i ask a question? i have implemented this within my code and it works. but, when i minimize my form (which is started in the main thread) while my second thread (the one that runs looooong) is still running, i still cannot restore my form again. it sticks to the taskbar and thus the user cannot make the form reappear. argh..
do you have any idea what i'd have to improve in my code?
thanks! marc.enzler@freesurf.ch (If this comment was disrespectful, please report it.)
Marc, I would have to see your code. (If this comment was disrespectful, please report it.)
9/11/2003 3:20:52 AM:
hi jeroen!
thanks for your answer. i managed to get it running fine by adding begininvoke. i was updating a taskbar which run in the main thread (the form) directly from the second thread. that was bad and resulted in refresh problems as well as destroyed objects once the second thread had ceased to exist. begininvoke was the way to go. thanks anyway for your time!
marc (If this comment was disrespectful, please report it.)
3/14/2004 11:32:09 AM:
Thanks for the article...it was a great way of explaning the concept of free threading. (If this comment was disrespectful, please report it.)
8/21/2004 6:28:26 PM:
I am writing a windows service where a function would tak long time to execute\. So wsay i created than class::method on a new thread, OK. Now say administrator stops the service what will happen to that function running on an independant thread? (If this comment was disrespectful, please report it.)
5/3/2005 11:39:47 AM:
How can i create unilimited or say 100 threads without explicitly declaring 100 threads. I need to create a mass email sender application for a list server (If this comment was disrespectful, please report it.)
I have been scouring the Internet and books to see how to run a subroutine in a different thread and had no success until I stumbled over this article! This article is to-the-point and packed with useful information. Excellent! (If this comment was disrespectful, please report it.)
Add Your Feedback
Your feedback will be posted below and an email sent to
the author. Please remember that the author was kind enough to
share this with you, so any criticisms must be stated politely, or they
will be deleted. (For feedback not related to this particular article, please
click here instead.)