PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197

PHP User Warning: fetch_template() calls should be replaced by the vB_Template class. Template name: bbcode_highlight in ..../includes/functions.php on line 4197
Understanding Multi-Threading in VB.Net-VBForums
Page 1 of 4 1234 LastLast
Results 1 to 40 of 149

Thread: Understanding Multi-Threading in VB.Net

  1. #1

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Understanding Multi-Threading in VB.Net

    Some time ago I was inspired to write an article here about Lambdas and Delegates after observing a couple of posts where members voiced their misunderstanding of them. It seemed quite helpful to a couple of people and now I'm inspired yet a again to write about another troubling area in the .Net world, multi-threading. In this article I hope to be able to take someone who's only heard about it from the depths ignorance about it to being able to competently command their processor's cores to do what they were made to do, share the burden of executing one or more tasks simultaneously.

    Lets Begin

    Background

    To understand what multi-threading is you must first understand what happens when you run an executable program on an operating system. The OS loads the executable program into memory and the processor executes its code line by line. However with this simple logic one will realize that eventually the lines will run out and the program will end but when you open NotePad, it doesn't end until you tell it to so what gives ? This is because a program is basically one big loop where the same instructions keep getting executed over and over. The reality is actually far more complex that this but its the underlying idea. When you run NotePad, the program enters one big loop and which basically keeps checking for weather the user typed something or clicked a menu option and then it responds to these inputs by executing other code before returning to the loop. So if you press the letter 'K', the code leaves the loop and executes the code that is responsible to rendering the letter 'K' into NotePad's window. When this code is finished, it returns to the loop where it left off, waiting to process other messages.

    Think of the code looking like this:-
    vbnet Code:
    1. '
    2.     Public Sub Main()
    3.         Do
    4.             Dim msg As MESSAGE = GetMessage()
    5.  
    6.             If msg IsNot Nothing Then
    7.                 ProcessMessage(msg)
    8.             End If
    9.         Loop
    10.     End Sub

    The loop repeatedly checks for messages(key presses, mouse clicks etc) and when it finds one, it passes it to a procedure that knows how to take action. Its an intriguing design but what happens the action taken by ProcessMessage leads to another loop, one which will take several seconds or even minutes ? Eg. You click a hyperlink in Internet Explorer to download a file.

    Imagine that this is the code that Internet Explorer calls to download a file:-
    vbnet Code:
    1. '
    2.     Public Sub DownloadFile(ByVal url As String)
    3.  
    4.         Dim fileSize As Integer = GetFileSize(url)
    5.         Dim fileName As String = GetFileName(url)
    6.         Dim curPos As Integer = 0
    7.  
    8.         Do While curPos < fileSize
    9.             Dim fileData() As Byte
    10.  
    11.  
    12.             Dim bytesRecd As Integer = GetNextChunk(fileData)
    13.             curPos += bytesRecd
    14.  
    15.             appendToFile(fileName, fileData)
    16.         Loop
    17.  
    18.     End Sub

    Now thats not the actual code but I'd expect the concept to be similar to that, so just imagine that it is. What would happen ? Well notice that DownloadFile has a loop which repeatedly gets chucks of the file, so if this file is several gigabytes in size then this loop is going to be executing for quite a while. If clicking the download hyperlink led to that sub being executed by ProcessMessage then common sense would dictate that Internet Explorer would not be able to process any more messages until DownloadFile has finished executing and returned. This means that Internet Explorer would not respond to anything you do because the main message loop is halted. Visualize it like this:-
    vbnet Code:
    1. '
    2.     Public Sub Main()
    3.         Do
    4.             Dim msg As MESSAGE = GetMessage()
    5.  
    6.             If msg IsNot Nothing Then
    7.                 If msg.MsgType = FILEDOWNLOAD Then
    8.                     'imagine msg.Data has the url
    9.                     DownloadFile(msg.Data)
    10.                 End If
    11.             End If
    12.         Loop
    13.     End Sub
    Now I must re-iterate that this is not the actual code that does these things in Windows and IE. Its just to give you an idea of what happens.


    When DownloadFile is called the message loop cannot process additional messages until DownloadFile returns. However, we all know that we can download a file, even many files and still browse the internet. How can this be ? This is where multi-threading comes into play.

    What is multi-threading

    While many may take the fact that you can still browse while you have a file downloading in the background for granted, it is in fact multi-threading that gives you this luxury. When you have downloads in progress in Internet Explorer, these downloads are being handled by their own threads and the main browser window is running on a different thread so that each of these tasks can proceed independently. Going back to my earlier illustrations, what happens is that DownloadFile is passed off to another thread and returns immediately so the message loop can proceed as normal. Threads are exactly that, threads of execution. So what we have are two loops running at the same time. The reality though is that they are not running at the same time but Windows is actually switching between then very very quickly to give the impression of concurrency. The operating system may execute two lines of code in the message loop and switch to the download loop and execute two lines before switching back or switching to yet another thread doing something else. Note though, that on multi-core systems these two threads can actually be executing at the same time. This is what multi-threading is about, execution separate sections of code seemingly or actually at the same time. So now we come to the fun part, actually doing it which we will begin in the next post.
    Last edited by Niya; Jun 18th, 2012 at 03:09 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  2. #2

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    How its done

    We'll start off simple and then build on it. Let us begin with a simple operation. We are going to use a loop that counts from 1 to 100 to simulate a long running operation like a file download.

    Open a new Windows Forms project and place a Button and a Label on Form1. Double click the Button to bring up its Click event handler then code the event handler as:-
    vbnet Code:
    1. '
    2.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3.         For i = 1 To 100
    4.             Label1.Text = CStr(i)
    5.             Threading.Thread.Sleep(200)
    6.         Next
    7.     End Sub
    Execute the program and click the button. You should notice that the UI freezes until the count is actually complete. Notice that while its frozen, the label is not being updated. Why is this ? Well remember the message loop I mentioned in the Backround section of this article ?. Pressing the button executes the count loop so while this loop is running the message loop of the UI cannot execute and process the repaint messages for the label to update itself. The UI also cannot process mouse clicks, and key presses. You cannot even move the window.

    Our solution is to pass off the count to another thread to execute so that the UI's message loop remains unhindered. Remember, the UI's message loop is running in its own thread usually called the main thread or the UI thread.

    We'll start off by putting our counting operation into a separate sup:-
    vbnet Code:
    1. '
    2.     Private Sub Count(ByVal Max As Integer)
    3.         For i = 1 To Max
    4.             Label1.Text = CStr(i)
    5.             Threading.Thread.Sleep(200)
    6.         Next
    7.  
    8.     End Sub

    What we do next is really a matter of preference. Some people elect to use the BackgroundWorker component to do their multi-threading. I'm not going to show that approach. The forum is rich with examples on using the BackgroundWorker and I've always preferred to do my threading directly. This is the direction I'm going to demonstrate.

    Now that we have our count in its own sub. Code the Button's Click event as:-
    vbnet Code:
    1. '
    2.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3.         Dim t1 As New Threading.Thread(AddressOf Count)
    4.  
    5.         t1.Start(100)
    6.     End Sub
    The first line instantiates a new Thread object and passes it the address of the Count sub since this is the sub we want to execute on another thread. Its very important to note that here lies a very serious limitation. Only a sub with a single parameter is allowed. Luckily in our example Count only has one parameter, the number at which the count should end.

    The second line starts the thread. Start takes one parameter which it uses to call the sub hence:-
    vbnet Code:
    1. t1.Start(100)
    Is like:-
    vbnet Code:
    1. Count(100)
    Only it runs on a different thread from the thread it was called from.

    If you have Option Strict On the add this Count overload:-
    vbnet Code:
    1. '
    2.     Private Sub Count(ByVal Max As Object)
    3.         If TypeOf Max Is Integer Then
    4.             Count(CInt(Max))
    5.         End If
    6.     End Sub
    The reason is because the Thread class's constructor expects the address to a sub that takes one parameter of type object. Option Strict would not allow the address of the Count that takes an Integer as its parameter to be used. Now execute the program.

    At this point you should be ready to pull out your hair because you would get this error:-


    So just what in God's name is happening here ? Don't worry, that error is actually happening to prevent potential disasters. Remember I said that the UI has its own thread ? Well think about it what is the UI ? Its basically every thing to do with what you see. Labels, pictures, text are all UI elements so any control that is responsible for showing these things is a part of the UI. The windows message loop runs in the UI thread and its responsible for dealing with these controls. Its repainting, its responses to key presses and mouse clicks are all the UI's thread is responsible for. Its rather intrusive for another thread to just come out of nowhere and make a change to a control that its not responsible for. How does this foreign thread know what the UI is doing with any give control at any given moment ? It doesn't and if it interferes with a control while the UI was in the middle of making some change to the control then it can really cause some instability in the application if the control ends up in some invalid state. This is why VB.Net prevents us from changing the Label's text from another thread.

    The correct way to go about this is for the foreign thread to politely ask the UI's thread to update the Label for us. If the UI's thread is busy when the foreign thread makes the request, the request is simply queued and when the UI's thread is finish whatever its doing, its message loop will move on to process the other messages in the queue which would eventually lead it to the request to execute the code that would change the Label's text.

    Start by making a function to change the Label's text:-
    vbnet Code:
    1. '
    2.     Private Sub SetLabelText(ByVal text As String)
    3.         If Label1.InvokeRequired Then
    4.             Label1.Invoke(New Action(Of String)(AddressOf SetLabelText), text)
    5.         Else
    6.             Label1.Text = text
    7.  
    8.         End If
    9.     End Sub
    In this sub we call the Label's InvokeRequired property to determine if the Label is being accessed in the current sub from the UI thread or another thread. It returns True if it was called from a foreign thread.

    vbnet Code:
    1. Label1.Invoke(New Action(Of String)(AddressOf SetLabelText), text)
    The above line submits a request to the UI thread to re-run SetLabelText but instead of running it on the foreign thread, it would then execute on UI thread. InvokeRequired would then return False which would execute the Label's text change. This is the key because the UI thread would only execute the sub when its not busy processing some other message.

    All that's left now is to change Count:-
    vbnet Code:
    1. '
    2.     Private Sub Count(ByVal Max As Integer)
    3.         For i = 1 To Max
    4.             SetLabelText(CStr(i))
    5.             Threading.Thread.Sleep(200)
    6.         Next
    7.  
    8.     End Sub

    We use SetLabelText to change the Label's text instead of trying to change it directly and viola! We have successfully created a simple multi-threaded application.

    One thing remains. Execute the program, click the button and close the form before the count is finished. If you did this in the IDE, you would notice that the program doesn't actually stop. That's because the thread is a foreground thread. Usually, we want a background thread for operations like these. Background threads are terminated when the application terminates which is actually when all foreground threads terminate. The UI thread is a foreground thread and by closing the form we terminate that thread but the application won't terminate because the counter is running on another foreground thread. In this case its undesirable so we must make a change:-
    vbnet Code:
    1. '
    2.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3.         Dim t1 As New Threading.Thread(AddressOf Count)
    4.  
    5.         t1.IsBackground = True
    6.  
    7.         t1.Start(100)
    8.     End Sub

    We set IsBackground to True when we create the thread in the Button's Click event handler. Now, the application has only one foreground thread so the application ends when this thread terminates which automatically terminates all background threads.

    This is the most basic way to do straight threading in VB.Net. Now there are a couple things I still want to cover like thread pool threads, using threads to create asynchronous methods on classes, and using events to signal when operations on other threads are finished. I'll cover that on a later date in the next post. Until then folks, happy threading !!

    Note: The following attachment is the demo project. It was written using VS2008 so you must have VS2008 and later to open it.
    Attached Files Attached Files
    Last edited by Niya; Jul 3rd, 2012 at 10:55 PM. Reason: Minor grammer fix
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  3. #3

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Thread Pools
    One other thing I want to discuss are thread pools. As defined by wikipedia:-
    In computer programming, the thread pool pattern (also replicated workers) is where a number of threads are created to perform a number of tasks, which are usually organized in a queue. The results from the tasks being executed might also be placed in a queue, or if the tasks return no result (for example, if the task is for animation).Typically, there are many more tasks than threads. As soon as a thread completes its task, it will request the next task from the queue until all tasks have been completed. The thread can then terminate, or sleep until there are new tasks available.
    Stated simply, they are pre-created threads that may be continually recycled to be used by several tasks waiting in a queue. The need for it arises from the fact that creating new threads require some overhead, more so than re-using older threads. Its faster to re-use a thread than it is to create a new one. There are other performance related advantages to using thread pools beside creation overhead. You can read up more on thread pools in general here at wikipedia. Also, read up on thread pools in .Net here.

    Those two links provide most of what you need to know about thread pools so I'm not going to spend any more time talking about it. Lets just go straight to using them in VB.Net.

    If we were to alter the example I provided in post #2 to use thread pools, what do you think we need to do ? :-
    vbnet Code:
    1. '
    2.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3.         System.Threading.ThreadPool.QueueUserWorkItem(AddressOf Count, 100)
    4.     End Sub

    The above is how we would code the Button's Click event handler to start the count on a thread pool thread. Bet you didn't expect it would be so simple. In fact, its simpler than creating the thread and assigning it a task ourselves. There is no need to instantiate it and no need to make it a background thread. Thread pool threads are background threads by default in .Net.

    The QueueUserWorkItem is a shared method of the ThreadPool class. It takes a delegate to a sub that has one parameter of the Object type, just like the Start method of the Thread class.

    Multi-Threaded classes

    If you examine my example, I'd expect one to conclude that while it works, it isn't very neat. Personally, I'd never actually do multi-threading like that. What if I needed to change some other property of a control ? What if I needed to alter other controls ? Going with the pattern in the example, I'd have to write a function for every property I wish to change on any control because I simply cannot change this property as I've shown, from another thread other than the UI thread. If I wished to change the Text property of three different labels to three different values, I'd need a separate function to change each Label's Text property.

    Changing properties across threads requires a function to check the InvokeRequired property before changing the property.

    Another problem, what if I needed more than one counter ? Of course, you can simply click the button more than once and it would start multiple counts. But there is a practical problem with that. How do you tell them apart ? What you actually have are several threads running that for all intents and purposes have no identity and this is only magnified when you realize that once a thread pool thread starts, you really have no kind of access to it. It starts and does its thing. You can't peek into it, abort it, pause it, nothing.

    Associating a running thread with an instance of a class solves the identity problem. Classes by their very nature have unique identities. If you create say...two TextBox instances and use the Is operator to compare them, it would evaluate to False because while they are the same type of class they are unique individuals. They are the same control but they can have different text, different fonts, colours or sizes. It is the same with any class, you can even give them unique IDs in the form of a property if you so desire. In the next post, we would get into creating a multi-threaded class. Stay tuned.
    Last edited by Niya; Jul 5th, 2012 at 01:14 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  4. #4

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Add member to a String

    Writing a multi-threaded class

    For this we are going to use the same count sub but lets make things a little more interesting by changing it into a function that returns a value after its finished.
    vbnet Code:
    1. Public Class Counter
    2.  
    3.     Public Function Count(ByVal Max As Integer) As String
    4.         Dim startTime As DateTime = DateTime.Now
    5.  
    6.         For i = 1 To Max
    7.             Threading.Thread.Sleep(200)
    8.         Next
    9.  
    10.         Return "Count took : " + (DateTime.Now - startTime).ToString
    11.     End Function
    12.  
    13. End Class

    Our Count sub is now a function that returns a String that states the amount of time it took to complete.

    Now start a new Windows Forms project and add a new code file and within it place the above code. On the Form place a Label and a Button and code the Button's Click event handler as:-
    vbnet Code:
    1. '
    2.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3.         Dim cn As New Counter
    4.  
    5.         Label1.Text = cn.Count(20)
    6.     End Sub

    Run the program and click the button. You'd notice, just like the start of our previous project in post #2, you can't do anything until the count is finished. You can't move the form or click anything on it because the UI thread is currently executing the Count function so it can't process any mouse or keyboard messages until it is free from Count. When Count is finished, it returns a value which we display in the label.

    Before applying multi-threading to the class I want to address a limitation of functions like the Start method of the Thread class and QueueUserWorkItem of the ThreadPool class. These methods have a rather annoying limitation of only being able to take the address of functions with only one parameter and Option Strict On would demand this parameter be of the Object type. What if Count had more than one parameter ? We would have to write an overload to take one Object parameter and have that overload call the real Count, getting the parameter from an object made to hold the parameters as fields or properties. If you were to have other multi-threaded methods in the class then you would have to make a class a for the arguments of each method, and a method overload for each to take a single Object parameter made of one of these classes. In other words you have to create two extra entities(a method and a class) for every method you want to call on another thread. This is really retarded, there is just no excuse for that. MS should and could have made Start/QueueUserWorkItem more flexible. Thankfully I came up with a one-size fits all solution. One MS could have easily come up with.

    Outside of the Counter class add this class:-
    vbnet Code:
    1. '
    2. Public Class ThreadExtensions
    3.     Private args() As Object
    4.     Private DelegateToInvoke As [Delegate]
    5.  
    6.     Public Shared Function QueueUserWorkItem(ByVal method As [Delegate], ByVal ParamArray args() As Object) As Boolean
    7.         Return Threading.ThreadPool.QueueUserWorkItem(AddressOf ProperDelegate, New ThreadExtensions With {.args = args, .DelegateToInvoke = method})
    8.     End Function
    9.  
    10.     Private Shared Sub ProperDelegate(ByVal state As Object)
    11.         Dim sd As ThreadExtensions = DirectCast(state, ThreadExtensions)
    12.  
    13.         sd.DelegateToInvoke.DynamicInvoke(sd.args)
    14.     End Sub
    15. End Class

    I'm not going to go into the details of how that works because I'll have to explain way too much that has nothing to do with multi-threading at all. I'll just say what it does. Its simply a version of QueueUserWorkItem that can take the address of any sub or function and an arbitrary amount of arguments using ParamArray so we can call any method with it.

    Now going back to our counter class I'd start by adding this function:-
    vbnet Code:
    1. '
    2.     Public Sub CountAsync(ByVal Max As Integer)
    3.         ThreadExtensions.QueueUserWorkItem(New Func(Of Integer, String)(AddressOf Count), Max)
    4.     End Sub
    Its a kin function to Count that calls Count on a thread pool thread using our special QueueUserWorkItem. Now our class has two ways to call Count. One is synchronous and one is asynchronous. Notice that we declare the asynchronous one as a sub because it would return immediately after its called hence we cannot get a return value because the count would be taking place on another thread while the calling thread would proceed as normal.

    The next thing we want to do is to create a private field in our Counter class:-
    vbnet Code:
    1. Private context As Threading.SynchronizationContext = Threading.SynchronizationContext.Current
    Think of this object as an anchor into the UI thread. Our asynchronous Count would be running on another thread but remember we may want to report progress back to the UI thread by updating labels or progress bars. As demonstrated in post #2, we can't simply alter a control from any thread other than the UI thread so we need this object to submit methods to be executed on the UI thread. The SynchronizationContext class has a Send method which takes the address of the function we wish to invoke on the UI thread. Send however, suffers from the same limitation like QueueUserWorkItem, it can only the address of a function with only one parameter. We address this in the same manner as we did before:-
    vbnet Code:
    1. '
    2.     Public Shared Sub ScSend(ByVal sc As Threading.SynchronizationContext, ByVal del As [Delegate], ByVal ParamArray args() As Object)
    3.         sc.Send(New Threading.SendOrPostCallback(AddressOf ProperDelegate), New ThreadExtensions With {.args = args, .DelegateToInvoke = del})
    4.     End Sub
    Add the above shared method to the ThreadExtensions class. We will use the above method to execute methods on the UI thread so we can alter controls in the proper thread safe manner.

    Ok....so we're practically set. Now we need to set up a way to report the progress of the count and return the function's value. In the earlier example in post #2, we used a function to set the Label's text to reflect a where the count has reached. Now we don't want to have to write a separate function for every control we may wish to alter so a far more agreeable solution would be to raise some kind of event on the UI thread and then we are free to do anything to any control and all controls in the normal way. No need to call InvokeRequired to check for thread safety anymore. We pass of that responsibility to our Counter class.

    Now, lets get to coding the events. In this I tend to stick to the patterns of setting up events that is used by MS in the .Net Framework. So we start by creating an EventArgs based object:-
    vbnet Code:
    1. '
    2. Public Class CountChangedEventArgs
    3.     Inherits EventArgs
    4.  
    5.     Private _CurrentCount As Integer
    6.     Private _Max As Integer
    7.  
    8.     Public Sub New(ByVal cc As Integer, ByVal max As Integer)
    9.         _CurrentCount = cc
    10.         _Max = max
    11.     End Sub
    12.     Public ReadOnly Property CurrentCount() As Integer
    13.         Get
    14.             Return _CurrentCount
    15.         End Get
    16.     End Property
    17.     Public ReadOnly Property Max() As Integer
    18.         Get
    19.             Return _Max
    20.         End Get
    21.     End Property
    22. End Class

    Place the above class outside of the Counter class.

    Now add these lines of code to the Counter class:-
    vbnet Code:
    1. '
    2.     Public Event CountChanged As EventHandler(Of CountChangedEventArgs)
    3.     Protected Overridable Sub OnCountChanged(ByVal e As CountChangedEventArgs)
    4.         RaiseEvent CountChanged(Me, e)
    5.     End Sub

    Our Counter class now has a CountChanged event which we would now raise. Remember, we should raise the event on the UI thread so any code in the event handler would be free to alter controls without worrying about cross-thread calls:-
    vbnet Code:
    1. '
    2.     Public Function Count(ByVal Max As Integer) As String
    3.         Dim startTime As DateTime = DateTime.Now
    4.         Dim e As CountChangedEventArgs
    5.  
    6.         For i = 1 To Max
    7.             e = New CountChangedEventArgs(i, Max)
    8.  
    9.             If context Is Nothing Then
    10.                 OnCountChanged(e)
    11.             Else
    12.                 ThreadExtensions.ScSend(context, New Action(Of CountChangedEventArgs)(AddressOf OnCountChanged), e)
    13.             End If
    14.  
    15.             Threading.Thread.Sleep(200)
    16.         Next
    17.  
    18.         Return "Count took : " + (DateTime.Now - startTime).ToString
    19.     End Function

    Change the Count sub to the above. We use the ThreadExtensions function we wrote earlier to have the SynchronizationContext object raise the event on the UI thread for us in the case context is not Nothing. If context is Nothing we simply raise the event in the normal way. I do not understand the SynchronizationContext enough to tell you the circumstances under which it would be Nothing but in the most common scenarios it should have a proper object.

    The only thing remaining now is that return value. We simply use another event. Like before we create a new EventArgs object:-
    vbnet Code:
    1. '
    2. Public Class CountCompletedEventArgs
    3.     Inherits EventArgs
    4.     Private Dim _message As String
    5.  
    6.     Public Sub New(ByVal msg As String)
    7.         _message = msg
    8.     End Sub
    9.     Public ReadOnly Property Message() As String
    10.         Get
    11.             Return _message
    12.         End Get
    13.     End Property
    14.  
    15. End Class

    And add the following to the Counter class:-
    vbnet Code:
    1. '
    2.     Public Event CountCompleted As EventHandler(Of CountCompletedEventArgs)
    3.     Protected Overridable Sub OnCountCompleted(ByVal e As CountCompletedEventArgs)
    4.         RaiseEvent CountCompleted(Me, e)
    5.     End Sub

    Now we change our Count function yet again:-
    vbnet Code:
    1. '
    2.     Public Function Count(ByVal Max As Integer) As String
    3.         Dim startTime As DateTime = DateTime.Now
    4.         Dim e As CountChangedEventArgs
    5.         Dim msg As String
    6.  
    7.         For i = 1 To Max
    8.             e = New CountChangedEventArgs(i, Max)
    9.  
    10.             If context Is Nothing Then
    11.                 OnCountChanged(e)
    12.             Else
    13.                 ThreadExtensions.ScSend(context, New Action(Of CountChangedEventArgs)(AddressOf OnCountChanged), e)
    14.             End If
    15.  
    16.             Threading.Thread.Sleep(200)
    17.         Next
    18.  
    19.         msg = "Count took : " + (DateTime.Now - startTime).ToString
    20.  
    21.         If context Is Nothing Then
    22.             OnCountCompleted(New CountCompletedEventArgs(msg))
    23.         Else
    24.             ThreadExtensions.ScSend(context, New Action(Of CountCompletedEventArgs)(AddressOf OnCountCompleted), New CountCompletedEventArgs(msg))
    25.         End If
    26.  
    27.         Return msg
    28.     End Function

    Our Count function now raises a completed event and passes the return value using the EventArgs object. Our Counter class in now has multi-threaded capabilities.

    Now go back to the Form's code and change it to this:-
    vbnet Code:
    1. Public Class Form1
    2.     Private WithEvents m_cn As New Counter
    3.  
    4.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    5.         m_cn.CountAsync(100)
    6.     End Sub
    7.  
    8.     Private Sub m_cn_CountChanged(ByVal sender As Object, ByVal e As CountChangedEventArgs) Handles m_cn.CountChanged
    9.         Label1.Text = CStr(e.CurrentCount)
    10.     End Sub
    11.  
    12.     Private Sub m_cn_CountCompleted(ByVal sender As Object, ByVal e As CountCompletedEventArgs) Handles m_cn.CountCompleted
    13.         MsgBox(e.Message)
    14.     End Sub
    15. End Class

    The Form must have a Label and a Button. Run the application and click the button and you should see the count progressing indicated by the label and a message box pop up when the count is done, showing the return value which is the time the count took to complete. So there you have it. A working multi-threaded class. There is a little more I'd like to write on this subject but I'm too near the 15K character limit for this post so I'll have to cut it here. Please feel free to post any questions you may have in the thread. Good luck
    Attached Files Attached Files
    Last edited by Niya; Jul 19th, 2012 at 01:37 PM. Reason: Minor correction
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  5. #5
    Hyperactive Member marniel647's Avatar
    Join Date
    Aug 2010
    Location
    MSDN Library
    Posts
    259

    Re: Understanding Multi-Threading in VB.Net

    thanks for this post i also having a problem under multithreading. i will wait for your other post regarding multithreading.

  6. #6

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    You're welcome . The first two posts covers the heart of it. My next post will basically show how you can organize that neatly into classes to create multi-threaded components. It will take what I've already discussed and twist it up a little and it would also cover thread pools. I'll try to get it up within this week.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  7. #7
    Hyperactive Member marniel647's Avatar
    Join Date
    Aug 2010
    Location
    MSDN Library
    Posts
    259

    Re: Understanding Multi-Threading in VB.Net

    yeah. but i have a question that running into my mind
    why do we use threading.thread.sleep
    and what does this bold part do t1.start(100)?

    sorry for a noob question

  8. #8

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Fair questions. There is nothing wrong with not knowing

    Quote Originally Posted by marniel647 View Post
    why do we use threading.thread.sleep
    Thread.Sleep suspends the thread. I did it in that example because without it the count would have happened too fast for anyone to see what was happening. It should be noted that there is almost no practical reason to ever use Thread.Sleep in real applications. Its great for examples to demonstrate stuff but has no real benefits aside from that.

    Quote Originally Posted by marniel647 View Post
    what does this bold part do t1.start(100)?
    Remember I did this:-
    vbnet Code:
    1. Dim t1 As New Threading.Thread(AddressOf Count)
    That line created the Thread object. The constructor takes the address of the Count sub. If you look back you would see that sub Count takes one argument, which is the number to count up to before the loop finishes. When you call Start it allows you to pass one parameter to whatever sub its expected to execute on a different thread, in this case its Count. So:-
    vbnet Code:
    1. t1.Start(100)
    That tells the thread object to call Count passing in 100 to its parameter. In other words it effectively does this:-
    vbnet Code:
    1. Count(100)
    Except that it runs on another thread. Makes sense ?
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  9. #9
    Hyperactive Member marniel647's Avatar
    Join Date
    Aug 2010
    Location
    MSDN Library
    Posts
    259

    Re: Understanding Multi-Threading in VB.Net

    Quote Originally Posted by Niya View Post
    Fair questions. There is nothing wrong with not knowing



    Thread.Sleep suspends the thread. I did it in that example because without it the count would have happened too fast for anyone to see what was happening. It should be noted that there is almost no practical reason to ever use Thread.Sleep in real applications. Its great for examples to demonstrate stuff but has no real benefits aside from that.



    Remember I did this:-
    vbnet Code:
    1. Dim t1 As New Threading.Thread(AddressOf Count)
    That line created the Thread object. The constructor takes the address of the Count sub. If you look back you would see that sub Count takes one argument, which is the number to count up to before the loop finishes. When you call Start it allows you to pass one parameter to whatever sub its expected to execute on a different thread, in this case its Count. So:-
    vbnet Code:
    1. t1.Start(100)
    That tells the thread object to call Count passing in 100 to its parameter. In other words it effectively does this:-
    vbnet Code:
    1. Count(100)
    Except that it runs on another thread. Makes sense ?
    thanks for your explanation now i know much the basic of multithreading
    thank you very much.

  10. #10
    Frenzied Member
    Join Date
    Jul 2006
    Location
    MI
    Posts
    1,837

    Re: Understanding Multi-Threading in VB.Net

    Quote Originally Posted by Niya View Post
    How its done

    vbnet Code:
    1. '
    2.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    3.         Dim t1 As New Threading.Thread(AddressOf Count)
    4.  
    5.         t1.Start(100)
    6.     End Sub

    Niya ... can I call a function here instead of a sub? If so, then how do I get the returned value?

    ie.
    Code:
    Dim t1 As New Threading.Thread(AddressOf Count)  'a function that returns a value.
    Last edited by nbrege; Jun 28th, 2012 at 02:23 PM.

  11. #11

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Well, a return value is the issue here isn't it. With Option Strict Off you can actually use a function with only one parameter but then what's the point. The function is executed on another thread so the thread that started isn't blocked waiting for a return value. The correct way to return a value would be to have the function itself signal its own completion and return a value by using a call back. My preferred approach is to implement the functionality that I wish to multi-thread into a class and use events to signal completion and return results. My reserved post is to write about and demonstrate that very thing. I'll try to get it up on Saturday, do you urgently need this information ?
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  12. #12
    Frenzied Member
    Join Date
    Jul 2006
    Location
    MI
    Posts
    1,837

    Re: Understanding Multi-Threading in VB.Net

    Saturday would be just fine. Thanks again...

  13. #13
    Frenzied Member
    Join Date
    Jul 2006
    Location
    MI
    Posts
    1,837

    Re: Understanding Multi-Threading in VB.Net

    Still waiting for your next post ...

  14. #14

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Yea I've already begun writing it. I got the section on thread pool threads already completed. I'm going to complete it today. Hope it doesn't disappoint
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  15. #15

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Updated....Posted the section on thread pools. Only one more section to go.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  16. #16
    Hyperactive Member marniel647's Avatar
    Join Date
    Aug 2010
    Location
    MSDN Library
    Posts
    259

    Re: Understanding Multi-Threading in VB.Net

    thanks for the update Niya reading now..

  17. #17

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Ok....I've completed the section on multi-threaded classes. Hope you like it
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  18. #18
    New Member
    Join Date
    Jul 2012
    Posts
    2

    Re: Understanding Multi-Threading in VB.Net

    great update Niya. I've been having trouble with implementing multi-threaded classes so your write up has been very helpful. I do have a quick question. What if I want to cancel the running operation in the middle?

  19. #19

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Its quite easy. Create a method to do it. In the case of the Counter class example, I'd make a CancelCountAsync method. I would then make a private boolean field called CountAsyncCancelled. When the CancelCountAsync method is called it would simply set our CountAsyncCancelled to True. The counter loop would be checking this on every iteration and if its True, we simply raise an event and exit the loop or return from the Count function. You could opt to create a cancel type event or use the completed event after adding something to the EventArgs object to indicate that the operation was cancelled.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  20. #20
    New Member
    Join Date
    Jul 2012
    Posts
    2

    Re: Understanding Multi-Threading in VB.Net

    wow that was easy. It took about 2 minutes to implement a cancel button in your counter example. I'm actually a little embarrassed that I didn't figure that out . Thanks for your help Niya.

  21. #21

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    That's understandable. The most natural instinct would be to stop the thread itself but its easy to lose sight of the fact that simply ending the function/sub the thread is running will end the thread itself which is what would essentially happen when a cancel is implemented the way I suggested.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  22. #22
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    Hello Niya, can you help me in my vb multithreaded application? I need to some help because this argument is a bit hard. I'm making a network application (server/client), and in the server app code I have to use multithreading. First, I made a new class which is composed by Tcp sockets code and some of graphical elements (declared after class declaration, a panel, a picture box and a button). I've declared an TCPlistener too, but It make his work into a BackGround Worker. It is composed by a do-loop cycle, which check for new connections, and when a new connection is coming, it create a new instance of my class... Now the problem it's how to interact with GUI from the class istances... how can i insert graphical elements to the GUI and control them? Thanks for the guide! =)

    P.S. Sorry for my bad english!

  23. #23

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Lemme see if I understand your problem. Are these classes being created on a different thread from the UI thread via the BackGroundWorker ?
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  24. #24
    Junior Member
    Join Date
    Jul 2012
    Posts
    21

    Re: Understanding Multi-Threading in VB.Net

    Thanks Niya, I'm working through your samples and are finding it quite enjoyable and very easy to understand, keep up the good work!

  25. #25
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    Quote Originally Posted by Niya View Post
    Lemme see if I understand your problem. Are these classes being created on a different thread from the UI thread via the BackGroundWorker ?
    Exactly! You understood me!! Can you help me?

  26. #26

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Quote Originally Posted by walterwjl View Post
    Thanks Niya, I'm working through your samples and are finding it quite enjoyable and very easy to understand, keep up the good work!
    Thanks, I'm glad you found it helpful

    Quote Originally Posted by psoftware View Post
    Exactly! You understood me!! Can you help me?
    Hmm....Well this is a little tricky. As I've shown in my samples, I like to communicate with the UI thread by raising events on the UI thread.

    Now I assume you have the BackgroundWorker component on a Form and in the DoWork event, your classes are being created. DoWork runs on another thread so classes created there will be on that thread. What you could do is create a constructor for your class that takes a SynchronizationContext object which it could use to raise events on the UI thread. You then create a private field in the Form itself which would hold the SynchronizationContext for the UI thread:-
    vbnet Code:
    1. Public Class Form1
    2.     Dim context As SynchronizationContext = SynchronizationContext.Current
    3.  
    4.     'Form code
    5.  
    6. End Class

    Use that context variable when calling the constructor of your class within the DoWork event. Observe my samples to learn how to use the SynchronizationContext to raise events on the thread it belongs to.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  27. #27
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    I tried to implement your guide in my code, but now there's some problems... I created some events and inserted your ThreadExtensions class, but when i try to raise an event (following your guide), context variable is null, and because of this, the event is raised in the background thread..... I inserted a MsgBox(Thread.CurrentThread.ManagedThreadId) in the load handle sub of my main window, and another in the events which I created, and the results are not equals... Why is context variable in null?

  28. #28

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    context would be null if you if you called Current on a thread without a SynchronizationContext object. You indicated that the object itself is being created on another thread so I'd expect it to be null. When a VB.Net program starts one of the things it does is create a SynchronizationContext for the UI thread. Like I said in my guide, think of it like an anchor. You wish to raise events on the UI thread so you get one from the UI thread by calling SynchronizationContext.Current on the UI thread. Since you create your objects on the thread pool thread that the BackGroundWorker uses in DoWork, the object's constructor would called on that thread which has no SynchronizationContext object. This is why I said in post #26 to declare it on the Form where the BackGroundWorker resides and create a constructor for your class that can take a SynchronizationContext object and when you create a new object in the DoWork event you call that constructor an pass the SynchronizationContext variable you declared on the Form.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  29. #29

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Here is an example of what I mean:-
    vbnet Code:
    1. Public Class WhatEverClass
    2.  
    3.     Private context As Threading.SynchronizationContext = Threading.SynchronizationContext.Current
    4.  
    5.     Public Sub New()
    6.  
    7.     End Sub
    8.  
    9.     'If this class is being instantiated on a non UI thread
    10.     'call this constructor and pass the SynchronizationContext
    11.     'your self
    12.     Public Sub New(ByVal context As Threading.SynchronizationContext)
    13.         Me.context = context
    14.     End Sub
    15.  
    16. End Class

    You call that second Sub New and pass it a SynchronizationContext object from the UI thread.

    This is what your Form code should resemble:-
    vbnet Code:
    1. Public Class Form1
    2.     'Obtain the Synchronization object for the UI
    3.     Private context As Threading.SynchronizationContext = Threading.SynchronizationContext.Current
    4.  
    5.     Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    6.  
    7.         Dim wec As New WhatEverClass(context)
    8.  
    9.     End Sub
    10. End Class

    Notice that I pass context declared as a private field in the Form, into the object's constructor since DoWork runs on another thread.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  30. #30
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    It's a bit hard because I have to translate and to read and read what you write... Now I understood you and I implemented this code in my project, It works fine. But I noticed only first created class can raise events, some others can't (handles subs aren't called)....In my form class I implemented the AddHandler function to make some functions handle my new class events. I don't understand this thing, It's very strange and make me angry!
    Sorry If I'm doing a lot of questions to you, but It's a bit hard argument and I don't know how to solve this problems. I hope you won't hate me!


    P.S. You're the best!

  31. #31

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Ok lets take this one problem at a time......are you having problems raising the events of the objects you create ?
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  32. #32
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    Mmmmmmh yes! Some classes, can't raise events, and I don't know why...

    This is my DoWork Sub

    Code:
    Private Sub ChechConnectionsThread_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles ChechConnectionsThread.DoWork
            While True
                Dim clientuser As New RemoteClient(ClientScreenSettings.TCPlistener.AcceptTcpClient, GenIndex(), context)
                AddHandler clientuser.ClientConnected, AddressOf ClientConnectEvent
                AddHandler clientuser.ClientDisconnected, AddressOf ClientDisconnectEvent
                AddHandler clientuser.ClientScreenChanged, AddressOf ClientSendScreen
                AddHandler clientuser.ClientSessionChanged, AddressOf ClientSessionChanged
            End While
        End Sub
    and this is one of the events...

    Code:
    Private Sub ClientConnectEvent(ByVal sender As System.Object, ByVal e As ClientConnectedEventArgs)
            MsgBox(e.ClientName + "id: " + e.ClientIndex.ToString + " has connected")
    End Sub

    I call the event from classes from this sub
    Code:
    RaiseEventConnected()
    Code:
    Public Sub RaiseEventConnected()
            Dim e As New ClientConnectedEventArgs(_ClientNick, _ClientIndex)
            If context Is Nothing Then
                OnClientConnectedEventArgs(e)
            Else
                ThreadExtensions.ScSend(context, New Action(Of ClientConnectedEventArgs)(AddressOf OnClientConnectedEventArgs), e)
            End If
        End Sub
    ClientConnectedEventArgs and OnClientConnectedEventArgs are coded like your code... Is there something wrong?

  33. #33
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    In ClientConnectEvent sub I inserted only the msgbox function to test the raising of the event, there are some other things into it...

  34. #34

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Whoa You do realize that by you can only have one RemoteClient object that way right ?

    Every time that loop iterates, the last RemoteClient object you created is up for garbage collection so its no wonder only one is raising events. As far as I see you implemented the threading properly so that's not the problem.

    Now, just get your hands dirty and complicate this a little more. Store every new RemoteClient you create in a List(Of T) declared as a private field of the Form. This would ensure that every RemoteClient created in that While loop is not lost but have active references:-
    vbnet Code:
    1. Public Class Form1
    2.     Dim ConnectedClients As New List(Of RemoteClient)
    3.  
    4.     Private Sub ChechConnectionsThread_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles ChechConnectionsThread.DoWork
    5.         While True
    6.             Dim clientuser As New RemoteClient(ClientScreenSettings.TCPlistener.AcceptTcpClient, GenIndex(), context)
    7.             AddHandler clientuser.ClientConnected, AddressOf ClientConnectEvent
    8.             AddHandler clientuser.ClientDisconnected, AddressOf ClientDisconnectEvent
    9.             AddHandler clientuser.ClientScreenChanged, AddressOf ClientSendScreen
    10.             AddHandler clientuser.ClientSessionChanged, AddressOf ClientSessionChanged
    11.  
    12.             ConnectedClients.Add(clientuser)
    13.         End While
    14.     End Sub
    15.  
    16. End Class
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  35. #35
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    I had already created a list of RemoteClient, but in RemoteClient class.....

    VB.NET Code:
    1. Public Class RemoteClient
    2.     Shared WithEvents AllClients As New List(Of RemoteClient)
    3.  
    4. End Class

    and I add classes to the list only when they send their nickname, in this manner...(then, they raise the connectedclient event)

    VB.Net Code:
    1. AllClients.Add(Me)
    2.               RaiseEventConnected()

    Is this the problem?

  36. #36

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    It shouldn't be a problem. Actually using a static field to record the connected clients is a very good idea. At this point I'm beginning to believe that your problem isn't related to threading but to some implementation detail elsewhere. Post all the code for the RemoteClient class as well as all the form code where the BackGroundWorker resides.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  37. #37
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    I made some changes in my code and now events seem to be raised correctly But now can I ask you for another thing?

    If I want to call subs from the UI thread to the BackgroundWorker Thread and to the classes?

    Thanks for all this Answers!

  38. #38

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Well what about it ? If you want to call a sub from the UI then you are free to do so. Is there some concern you have with this ?
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

  39. #39
    New Member
    Join Date
    Jul 2012
    Posts
    15

    Re: Understanding Multi-Threading in VB.Net

    Ehm.... Should I take the SynchronizationContext from the BackgroundWorker and use it with ScSend sub and my class function?

  40. #40

    Thread Starter
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    5,625

    Re: Understanding Multi-Threading in VB.Net

    Quote Originally Posted by psoftware View Post
    Ehm.... Should I take the SynchronizationContext from the BackgroundWorker and use it with ScSend sub and my class function?
    Ok, I'm a little lost now . I think you may be misunderstanding something fundamental here though. Tell me what sub you wish to call, what class it belongs to and where you are calling it from. I need some more details, I'm not really clear on what your concern is.


    Quote Originally Posted by psoftware View Post
    If I want to call subs from the UI thread to the BackgroundWorker Thread and to the classes?
    This statement makes no sense, you don't call subs from somewhere to somewhere. I just need you to clarify what you mean
    Last edited by Niya; Jul 25th, 2012 at 01:27 PM.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena


    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. -jmcilhinney

Page 1 of 4 1234 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width