Results 1 to 38 of 38

Thread: [RESOLVED] Multithreaded class

  1. #1

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Resolved [RESOLVED] Multithreaded class

    So far I understand the basics of threads and how to use simple subs in threads. I now need to assign a function to a thread, and then queue another one.
    My example is a computer search program that I'm working on right now. I know this is a lot of code, but It's easier to see what I'm trying to achieve this way.

    This code belongs to a button which executes the search
    Code:
    Public var_locList As New List(Of String) 'I put paths into this
    
    Dim var_temp As List(Of String) = task_MapoutDirectories(var_locList)
    'Then display the var_temp of course... but that is obvious
    Also, this is what I want to do, but It doesn't work this way:
    Code:
    Dim thread_dirSearch As System.Threading.Thread
    thread_dirSearch = New System.Threading.Thread(AddressOf task_mapoutDirectories(var_locList))
    thread_dirSearch.Start()
    First function (first task) - Finds all dirs and sub dirs in the specified paths and returns the complete list
    Code:
      Public Function task_mapoutDirectories(ByVal var_newList As List(Of String)) As List(Of String)
    
            Dim var_treeList As New List(Of String)
            Dim var_tempList As New List(Of String)
            Dim var_done As Boolean = False
    
            Do
                For Each var_location In var_newList
                    Try
                        var_tempList.AddRange(Directory.EnumerateDirectories(var_location, "*", SearchOption.TopDirectoryOnly).ToList)
                    Catch ex As Exception
                    End Try
                Next
    
                If var_tempList.Count = 0 Then
                    var_done = True
                Else
                    var_newList.Clear()
                    var_newList.AddRange(var_tempList)
                    var_treeList.AddRange(var_tempList)
                    var_tempList.Clear()
                End If
            Loop Until var_done
    
            Return var_treeList
        End Function
    Second function (second task) - Searches the directory list that I mapped out before for all files matching the search pattern
    - This one is not optimized yet, I'm figuring out how to get rid of some of the 'for' statements

    Code:
        Public Function task_findFiles(ByVal var_locList As List(Of String), ByVal var_keyList As List(Of String), ByVal var_typeList As List(Of String)) As List(Of String)
    
            Dim var_fileList As New List(Of String)
            Dim var_matchList As New List(Of String)
            If var_typeList.Count = 0 Then var_typeList.Add("*")
            If var_keyList.Count = 0 Then var_keyList.Add("*")
    
            For Each var_type In var_typeList
                For Each var_key In var_keyList
                    For Each var_location In var_locList
    
                        Dim var_searchPattern As String = String.Concat("*", var_key, "*")
                        Try
                            var_fileList.AddRange(IO.Directory.GetFiles(var_location, var_searchPattern, SearchOption.TopDirectoryOnly))
                        Catch ex As Exception
                        End Try
    
                        For Each var_file In var_fileList
                            If var_file.Contains(var_type) Or var_type = "*" Then var_matchList.Add(var_file)
                        Next
                        var_fileList.Clear()
                    Next
                Next
            Next
    
            Return var_fileList
        End Function
    Thanks for your answers in advance

  2. #2
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,390

    Re: Multithreaded class

    Rather than using raw threads, I think you would benefit greatly from taking a bit of time looking at Tasks. Tasks are lighter weight than threads, though a bit different. They are a relatively recent addition to the language, but I think that you would find them quite useful for what you are trying to do.

    What you are doing doesn't look so wrong to me. There is no point in passing in an argument to task_mapoutDirectories, because the argument you are passing in is located at class scope, so it is accessible by the thread as well as the UI thread, and that seems fine. I would guess that is where your problem is coming from, though you didn't state why the code wasn't working. Still, I suspect that you'd find an easier solution using Tasks...once you get past the relatively minor learning curve.
    My usual boring signature: Nothing

  3. #3

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Quote Originally Posted by Shaggy Hiker View Post
    Rather than using raw threads, I think you would benefit greatly from taking a bit of time looking at Tasks. Tasks are lighter weight than threads, though a bit different. They are a relatively recent addition to the language, but I think that you would find them quite useful for what you are trying to do.

    What you are doing doesn't look so wrong to me. There is no point in passing in an argument to task_mapoutDirectories, because the argument you are passing in is located at class scope, so it is accessible by the thread as well as the UI thread, and that seems fine. I would guess that is where your problem is coming from, though you didn't state why the code wasn't working. Still, I suspect that you'd find an easier solution using Tasks...once you get past the relatively minor learning curve.
    Ow, yeah, I didn't write what is wrong. So, when I'm trying to do this:
    Code:
    thread_dirSearch = New System.Threading.Thread(AddressOf task_mapoutDirectories(var_locList))
    It doesn't work with the argument "(var_locList)" and that is a problem because it simplifies a large portion of the code (less variables, etc.). I don't know if Tasks will allow this, but I'll look into them. The error that It gives is "AddressOf' operand must be the name of a method (without parentheses)"
    Also, there was another problem I was experiencing. In my function I had a piece of code that counted the number of directories and displaying it in real time but It was lagging the whole cycle, any way to use another thread or something to refresh it less often (it was just changing the text in a label)?
    Last edited by FireDust; May 12th, 2015 at 10:49 AM.

  4. #4

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Also, it's a function (task_mapoutDirectories), so there is a list of string that it returns, how do I assign it to a variable?

  5. #5
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,390

    Re: Multithreaded class

    Well, you don't. That would defeat the purpose of the threading. After all, if you were to write a line like this:


    Dim someVar = SomeFunction()
    someOtherVar = someVar

    You would expect that SomeFunction would be executed and the return would be put into someVar. The next line would then do something with someVar. However, what you are doing is launching a different process that will do a bunch of work. You certainly don't want execution to stop on the thread.Start line until the process completes, as that would defeat the whole purpose of threading. Instead, you want execution to launch the process and move right on to the next line. If the next line were to use the return from the function, as in my example, it wouldn't work, because the process was still running. The time when that variable becomes available for the original process isn't known, so you can't know when the next line could be executed (unless you wrote some code to respond to some event from the thread, or joined the thread, or something like that). Therefore, you can't usefully return anything from a thread. What you'd have to do is put the information into a variable at class or form scope (such as your list), and have the thread signal the UI thread that it had completed. Tasks makes that a bit easier because you can chain actions together such that when the Task completes it has the next steps that are to be taken.

    As for passing in the address of a method, that's all it is: An address to a function. That simply doesn't have enough information to allow for arguments to be passed, nor can it. Take a look at this link for examples of how you can pass arguments (except that it doesn't appear to be passing anything at all, but just relying on non-local variables):

    https://msdn.microsoft.com/en-us/library/wkays279.aspx
    My usual boring signature: Nothing

  6. #6

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Quote Originally Posted by Shaggy Hiker View Post
    Well, you don't. That would defeat the purpose of the threading.
    Ok, I was kinda expecting that, but never saw anybody explain why or if there is any other option other than a public variable. Now, i found something like this:
    Code:
     
    'The declare and then run version probably does the same thing...
    
    Await Task.Run(Sub()
    task_MapoutDirectories()
                   End Sub)
    of course it would be in a asynchronous sub. But this would stop the sub, and wait for the Task to finish, correct? If so, how do I run this and not lock up that sub?

    Also, after I launch task_MapoutDirectories() I need a timer running to read the public variable updated by this sub. If I put the timer code into the sub, it just slows it down dramatically. The timer I want should run 10-15 times a second while the sub would run at a few thousand times a second. Any easier way than running another asynchronous sub?
    If it's confusing I'll post some fictional code.

  7. #7
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,390

    Re: Multithreaded class

    Yeah, it's a bit confusing, but largely because I'm wondering what the timer is for. My assumption, which may be wrong, is that you are using the timer to poll for changes. That isn't normally ideal. It sounds like the sub will be adding things to a collection largely as fast as it can, whereas the foreground process will only be examining the collection periodically. If that's even close to being correct, you might want to look into the Queue(of String) rather than a list. My thinking would be that the sub would add items to the queue (perhaps only doing so if the item isn't already in the queue), then the foreground thread can just pop items off the queue if there are items to pop. That may or may not suit your needs.

    I would start the Task and fire it off. Await will do just as you expect: Await the completion of the task, which doesn't seem like it does what you want, as it would freeze the foreground process. What you might consider is having the background process raise events on the UI process. In other words, the background process could alert the UI process of...well, whatever it makes sense to alert it of. For example, as the last act of the Task, it could raise an event on the UI thread. It sounds like the Task will be repeated so often that the UI wouldn't even want to know about EVERY time the Task completed, but something like that might be of interest. If you'd like to see an example of one way to do that, you can look for the UDP class I posted in the .NET CodeBank (just search for threads started by me in that forum, as I don't have all that many). One thing I was doing in that class was launching a thread that sat and listened for incoming messages. When one came in, it processed it, raised an event, and exited. Upon receiving the event, the handler would start a new thread to sit and listen again. That isn't quite what you are doing, but the pattern of doing something, raising an event when finished, and having the handler start the process over, seems similar.
    My usual boring signature: Nothing

  8. #8

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Close but not quite, right now, everytime the background thread finds a set of new files/directories it updates the UI thread (using Invoke) with their total count. If I had to guess, It would be around 1000 times a second.
    Now, I want to have a timer that will periodically check the directory/file count in the list so it doesn't lag the UI thread so much. Example of my current 'update' code:
    Code:
    statusDisplay.obj_D_label.Text = String.Concat("Files Matching: ", var_matchList.Count)
    I also had another problem, I went from tasks, to using good old threading because it has a easy way to Abort it

    Code:
    'I went from
    
    Await Task.Run(Sub()
                  task_mapoutDirectories()
               End Sub)
    
    'to
    
    Private thread_dirMapout As Threading.Thread
    thread_dirMapout = New Threading.Thread(AddressOf task_MapoutDirectories)
    thread_dirMapout.Name = "dirMapout Thread"
    thread_dirMapout.Start()
    thread_dirMapout.Join()
    but now It just locks up the whole assembly and I can't find a reason why.. It actually executes the Start command. Thread actually starts running, but I tracked the problem to here:
    Code:
        Private Sub task_MapoutDirectories()
            MsgBox("Running [1]")
            If InvokeRequired Then
                MsgBox("Running [2]")
                Me.Invoke(New MethodInvoker(AddressOf task_MapoutDirectories))
                MsgBox("Running [3]")
            Else
                MsgBox("Running [4]")
            End If
        End Sub
    With the Await Task method I get 1; 2; 1; 4; 3; and then thread closes - Everything normal
    With Thread method I get 1; 2; and then it locks up, and does absolutely nothing
    Any ideas why? I would hate to read my own long posts, but it's kinda complicated and makes no sense... or I'm simply forgetting something basic :P
    Last edited by FireDust; May 13th, 2015 at 04:09 AM.

  9. #9

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    I'm still having problems with "Me.Invoke(New MethodInvoker(AddressOf task_MapoutDirectories))" locking up my whole assembly. There is probably some other way than invoking, but I only know of turning off "Control.CheckForIllegalCrossThreadCalls", but I really don't want to do that. Any other ideas?

  10. #10
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    Not followed this thread in its entirety, but a couple of observations about your post#8
    Code:
    thread_dirMapout = New Threading.Thread(AddressOf task_MapoutDirectories)
    thread_dirMapout.Name = "dirMapout Thread"
    thread_dirMapout.Start()
    thread_dirMapout.Join()
    The Join will block your UI thread until your thread_dirMapout thread finishes. But
    Code:
    Private Sub task_MapoutDirectories()
        MsgBox("Running [1]")
        If InvokeRequired Then
            MsgBox("Running [2]")
            Me.Invoke(New MethodInvoker(AddressOf task_MapoutDirectories))
            MsgBox("Running [3]")
        Else
            MsgBox("Running [4]")
        End If
    End Sub
    the Invoke attempts to marshal execution from the thread_dirMapout thread back onto the UI thread. The UI thread is still blocked, so nothing happens.

    If the operation you are performing in your Sub task_MapoutDirectories() is a long one (and it sounds like it might be), should you really be marshaling its execution back onto the UI?

  11. #11

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Even with the code I provided for Sub task_MapoutDirectories() it froze, and that sub has literally nothing to do.
    I use Invoke because I want to communicate and change things (currently only a label) in a different thread, and that always gives me a CrossThreadCall error, and there has to be a different way than turning off Control.CheckForIllegalCrossThreadCalls.

  12. #12
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,401

    Re: Multithreaded class

    Turning off Control.CheckForIllegalCrossThreadCalls??? You should never use it ever.
    My Github - 1d3nt

  13. #13
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    Quote Originally Posted by FireDust View Post
    Even with the code I provided for Sub task_MapoutDirectories() it froze, and that sub has literally nothing to do.
    My point was that your application freezes because Join is a blocking method that blocks the UI thread, and Invoke is a blocking method that blocks the non-UI thread. There's no where for the code execution to go.

    You could try replacing the call to Invoke with a call to BeginInvoke, which is not a blocking method. That should allow the non-UI thread to continue and finish, which in turn would release the UI thread from the Join statement.


    Quote Originally Posted by FireDust View Post
    I use Invoke because I want to communicate and change things (currently only a label) in a different thread, and that always gives me a CrossThreadCall error, and there has to be a different way than turning off Control.CheckForIllegalCrossThreadCalls.
    Indeed. NEVER call CheckForIllegalCrossThreadCalls unless you are upgrading old .NET 2.0 code which used a different mechanism when it came to cross thread calls to methods on Controls.

    If you want to update a Label from a non-UI thread, supply a separate method to update the Label and call that method from the non-UI thread. You can then do the invoking within that method. What it looks like you are currently trying to do is to invoke the entire directory search method, not just the updating of the Label Control.

    So for example, using the code in post#10, you would add a method similar to:
    Code:
    Private Sub UpdateLabel(progress As String)
        If Label1.InvokeRequired Then
            Label1.Invoke(Sub() UpdateLabel(progress))
        Else
            Label1.Text = progress
        End If
    End Sub
    and change the non-UI code to:
    Code:
    Private Sub task_MapoutDirectories()
        MsgBox("Running [1]")
        
        UpdateLabel("some progress report")
           
        MsgBox("Running [3]")
          End If
    End Sub
    EDIT: sorry, I originally left the Invoke call in there by mistake
    Last edited by Inferrd; May 16th, 2015 at 06:21 PM.

  14. #14

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Great! BeginInvoke worked like a charm. Now I only need a easy way to do the next thing...
    This code updates the label, and everything is good, but the non-UI Thread runs so quickly it manages to lag the UI Thread with the amount of updates. I need a easy way to periodically check and update the label. I can think of 2 ways, but both have a significant downside. One based on another asynchronous thread for refreshes with a delay, the other one counting up an integer variable and then reseting it while also refreshing the label.

  15. #15
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,401

    Re: Multithreaded class

    Them Two ways are terribl. Cn you show us the full code that "lags the" UI. i cant see how this is possible.
    My Github - 1d3nt

  16. #16

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    The program searches for every single directory and adds it to a list. Then it updates the form with how many it found.

    Code:
        Private Sub task_MapoutDirectories()
    
            If InvokeRequired Then
                Me.BeginInvoke(New MethodInvoker(AddressOf task_MapoutDirectories))
            Else
    
                Dim var_newList As New List(Of String)
                var_newList.AddRange(var_locList) 'locList is where the first directories are saved, and then searched
                Dim var_tempList As New List(Of String)
                Dim var_done As Boolean = False
    
                Do
                    For Each var_location In var_newList
                        Try 'Permision exceptions can pop up
                            var_tempList.AddRange(Directory.EnumerateDirectories(var_location, "*", SearchOption.TopDirectoryOnly).ToList)
                        Catch ex As Exception
                        End Try
                    Next
    
                    If var_tempList.Count = 0 Then
                        var_done = True
                    Else
                        var_newList.Clear()
                        var_newList.AddRange(var_tempList)
                        var_dirList.AddRange(var_tempList)
                        var_tempList.Clear()
                    End If
    
                    'Should be in a outside loop, too many refresh calls
                    statusWindow.obj_B_label.Text = String.Concat("Directories found: ", var_dirList.Count)
                    statusWindow.Refresh()
                Loop Until var_done
            End If
        End Sub
    This probably can be made a little more compact, but first I need to get it working properly

  17. #17
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    You're still missing the point that because you're calling Me.BeginInvoke(New MethodInvoker(AddressOf task_MapoutDirectories)), everything after the Else is running on the UI thread, not on a different worker thread.

  18. #18

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    This is how I call the task_mapoutDirectories sub.

    Code:
    Private thread_dirMapout As Threading.Thread
    thread_dirMapout = New Threading.Thread(AddressOf task_MapoutDirectories)
    thread_dirMapout.Name = "dirMapout Thread"
    thread_dirMapout.Start()
    thread_dirMapout.Join()
    It is still on the same thread? If so, then I really don't understand threads at all :P

  19. #19
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,401

    Re: Multithreaded class

    you do every thing on the UI thread.... it's clear you dont understand threads. Your code reads...

    ' do some thing
    'check if we are on a secondary thread
    'invoke to the ui
    ' do every thing on the ui
    My Github - 1d3nt

  20. #20

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    I'm guessing, that I do start a new thread, but because of the Invoke command it still forces it to run on the UI thread, correct? If so, I now understand why Inferrd creating a new sub, where only the label update was happening along with the Invoke. Gonna change that in the code. brb.

  21. #21
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    Is the task_MapoutDirectories() Sub part of a Form, and can you move that form around the desktop while the code is running?

    Edit: too slow

  22. #22

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Looks like this is going to work, but because I never worked with Invoke, I'm kinda lost here. I want to use a similar sub, to what you mentioned earlier.
    Code:
    Private Sub UpdateLabel(progress As String)
        If Label1.InvokeRequired Then
            Label1.Invoke(Sub() UpdateLabel(progress))
        Else
            Label1.Text = progress
        End If
    End Sub
    But I have multiple labels. Do I just stack them in the If command and invoke them all like so?

    Code:
    If Label1.InvokeRequired or Label2.InvokeRequired Then 'Also not sure if I should use "or" or "and"
            Label1.Invoke(Sub() UpdateLabel(progress, progress1))
            Label2.Invoke(Sub() UpdateLabel(progress, progress1))
    Doesn't really seem right to stack them, but that's why I'm asking

  23. #23
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    Quote Originally Posted by FireDust View Post
    But I have multiple labels. Do I just stack them in the If command and invoke them all like so?

    Code:
    If Label1.InvokeRequired or Label2.InvokeRequired Then 'Also not sure if I should use "or" or "and"
            Label1.Invoke(Sub() UpdateLabel(progress, progress1))
            Label2.Invoke(Sub() UpdateLabel(progress, progress1))
    Doesn't really seem right to stack them, but that's why I'm asking
    No. The Label1.Invoke marshals code execution onto the thread that Label1 was created on. All the other Controls on the same Form will be on the same thread, so any code that accesses them can safely be placed between the Else and End If
    Code:
    Private Sub UpdateLabel(progress As String)
        If Label1.InvokeRequired Then
            Label1.Invoke(Sub() UpdateLabel(progress))
        Else
            Label1.Text = progress
            Label2.Text = "some other string"
            TextBox1.Text = "what ever"
            '  and so on
        End If
    End Sub
    If you are updating multiple Controls, it might make it easier to follow if you change the sub to marshal execution onto the thread that the Parent Control (the Form in this case) is running on, along the lines of
    Code:
    Private Sub UpdateUI(progress As String)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() UpdateUI(progress))
        Else
            Me.Label1.Text = progress
            Me.Label2.Text = "some other string"
            Me.TextBox1.Text = "what ever"
            '  and so on
        End If
    End Sub
    Last edited by Inferrd; May 16th, 2015 at 08:13 PM. Reason: Got there in the end

  24. #24
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    One last thing: I'm fairly sure that the thread_dirMapout.Join() will be causing you problems by blocking the UI from updating until the worker thread finishes.

  25. #25
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,390

    Re: Multithreaded class

    Ya, Join will block. You only do that whaen you want to wait for a different process to complete before proceeding on the current process.
    My usual boring signature: Nothing

  26. #26

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    That is correct, I want to wait for that task to finish, then change some variables and move on to another task.
    Does it mean that the UI Thread will be blocked? I don't need to execute any code while the non-UI Thread is running, I just want it to be responding to draging the title bar, etc.

    Edit: Just changed and tested the code, it does indeed lock up until both of my tasks are finished, but I think I know a way around this.
    Last edited by FireDust; May 17th, 2015 at 03:48 AM.

  27. #27
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    39,390

    Re: Multithreaded class

    You might start a background thread that launches all the tasks. That background thread can then Join the tasks without penalty, but you certainly don't want the UI thread waiting for the background threads or you have defeated the whole purpose of threading. Basically, the UI thread must wait on nobody other than the user.
    My usual boring signature: Nothing

  28. #28
    PowerPoster Evil_Giraffe's Avatar
    Join Date
    Aug 2002
    Location
    Suffolk, UK
    Posts
    2,555

    Re: Multithreaded class

    If you are dealing with Tasks, you don't need any background threads. You create a Task and attach continuations to them - a continuation is just a function/sub that will run when the Task completes and has a value, or when the task errors or is cancelled (in which cases, obviously it won't have a result). You can attach a continuation to multiple tasks that can be fired when all of them have completed, or when the first of them completes, depending on your scenario. This means you do not have to wait on any thread for the tasks, they will automatically run whatever follow on code they have been told about.

    You can ask for these continuations to be run on the UI thread, which means you don't need to worry about invoking either.

    Tasks can be created with a cancellation token, that you can keep hold of and use to signal to the task to cancel. The task has to check this and exit itself cleanly, but calling Abort on a background thread is pretty horrific practice and you should be using a signal to let the background thread clean up of its own accord anyway.

  29. #29

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Alright, finally back to coding. Got it working with your help, though there is still a problem with the .Refresh() calls lagging the whole assembly. When I remove the whole refresh code, it becomes smooth and fast.

    Main non-UI Thread:
    Code:
        Private Sub task_MapoutDirectories()
    
            Dim var_newList As New List(Of String) : var_newList.AddRange(var_locList)
            Dim var_tempList As New List(Of String)
            Dim var_done As Boolean = False
    
            Do
                For Each var_location In var_newList
                    Try 'Permision exceptions can pop up
                        var_tempList.AddRange(Directory.EnumerateDirectories(var_location, "*", SearchOption.TopDirectoryOnly).ToList)
                    Catch ex As Exception
                    End Try
                Next
    
                If var_tempList.Count = 0 Then
                    var_done = True
                Else
                    var_newList.Clear()
                    var_newList.AddRange(var_tempList)
                    var_dirList.AddRange(var_tempList)
                    var_tempList.Clear()
                End If
    
                sub_refreshLabel(String.Concat("Directories found: ", var_dirList.Count), statusWindow.obj_C_label.Text, statusWindow.obj_D_label.Text, statusWindow.obj_statPBar.Value)
            Loop Until var_done
    
            'Directory search complete
            sub_editForms("Searching through directories", False, ProgressBarStyle.Continuous, 0, False)
            var_finishTime.Add(String.Concat("Directory mapping complete: ", CStr(DateTime.UtcNow)))
    
            'Start second task
            thread_fileSearch = New Threading.Thread(AddressOf task_findFiles)
            thread_fileSearch.Name = "fileSearch Thread"
            thread_fileSearch.IsBackground = True
            thread_fileSearch.Start()
        End Sub
    Sub that refreshes the label and a progress bar is sub_refreshLabel (obviously):

    Code:
        Private Sub sub_refreshLabel(ByVal var_B_text As String, ByVal var_C_text As String, ByVal var_D_text As String, var_pBarVal As Integer)
    
            If Me.InvokeRequired Then
                Me.BeginInvoke(Sub() sub_refreshLabel(var_B_text, var_C_text, var_D_text, var_pBarVal))
            Else
                statusWindow.obj_B_label.Text = var_B_text
                statusWindow.obj_C_label.Text = var_C_text
                statusWindow.obj_D_label.Text = var_D_text
                statusWindow.obj_statPBar.Value = var_pBarVal
                statusWindow.Refresh()
            End If
        End Sub
    I'm going to work on it, but I'm open to any suggestions.

  30. #30

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Quote Originally Posted by Evil_Giraffe View Post
    If you are dealing with Tasks, you don't need any background threads. You create a Task and attach continuations to them....
    Can you post just a few lines of code so I can see what you mean? Thanks

  31. #31
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,703

    Re: Multithreaded class

    Quote Originally Posted by FireDust View Post
    When I remove the whole refresh code, it becomes smooth and fast.
    Then don't use Refresh. Why would you need to do that anyway ?
    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

    Copy/move files using Windows Shell | I'm not wanted

    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

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  32. #32

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Because I'm exceptionally tired and busy (you can hear that in the video, I'm reeaally tired ), I decided to make a quick video about my problem. I think I explained everything, if not, please ask.
    Also, sorry that I nearly let the thread die


  33. #33
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    Hey, FireDust. Hope you're more awake now, you'll need to be......

    I was also going to question why you are using Refresh, but Niya beat me to it.
    From what you said in the video, it's to stop your status window from lagging; i.e., you can't drag it about the desktop, and the labels on it aren't updating when you feel they should be.

    This is going to be due to your UI thread being choked, which you might consider strange given that the whole point of your using multithreading is to prevent this.

    Well, it's probably down to your (mis)use of BeginInvoke. Yes, I know it was me who suggested its use, but when I suggested it, I was pointing out that your original complaint was caused by both your worker and UI threads being blocked at the same time: the UI thread by the call to Join, and the worker thread by the call to Invoke. So calling the non-blocking BeginInvoke instead would unjam your code (the alternative solution would have been not using Join in that situation).


    The basic difference between Invoke and BeginInvoke is that a call to Invoke blocks the worker thread while the code invoked on the UI thread runs. This obviously will result in the worker thread taking longer to run as, every time you update the UI from the worker, the worker waits for the update code to finish running on the UI thread.

    BeginInvoke, however, allows the worker thread to continue while the UI performs your update code at the same time.

    BeginIvoke might sound like a more optimal solution. Well it is, unless you use BeginInvoke to marshal execution onto the UI thread faster than the UI can execute that code. If you call BeginInvoke again, before the previous code has finished running, the UI chokes on your update code, it has no time to pump its messages, it never gets a chance to handle the mouse drags or redraw itself.

    Your use of Refresh "resolved" that situation by forcing an update of the whole Form, but it's a long way from being a good solution.


    So I think your main problem is that you are trying to update the UI too often. How you solve that is up to you. If you continue using BeginInvoke, you have to code your worker thread in such a way that you allow enough time between calls. Or you could go back to using Invoke which will force the worker to wait until the UI is ready before it tries to update it again. That will slow the worker down slightly, but how much longer it takes is down to how efficient the UI update code is, and how many times it is called.

  34. #34

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Thanks for the explanation . Now, my first instinct is to use another thread and Thread.Sleep([num]) to update the labels. Do you have any other(better) ideas how to do it?

  35. #35
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    Quote Originally Posted by FireDust View Post
    Do you have any other(better) ideas how to do it?
    Ideas? Yes.
    Good ideas? Probably not.

    You need to limit the frequency that you update the UI with progress info. My first thought would be to use a Stop Watch to give a timed interval between updates. E.g. using the most recent code you have given (post#29) and typed free hand, so not tested:
    Code:
       Private Sub task_MapoutDirectories()
    
            Dim var_newList As New List(Of String) : var_newList.AddRange(var_locList)
            Dim var_tempList As New List(Of String)
            Dim var_done As Boolean = False
    
            Dim sw as New StopWatch
            sw.Start
            Do
                For Each var_location In var_newList
                    Try 'Permision exceptions can pop up
                        var_tempList.AddRange(Directory.EnumerateDirectories(var_location, "*", SearchOption.TopDirectoryOnly).ToList)
                    Catch ex As Exception
                    End Try
                Next
    
                If var_tempList.Count = 0 Then
                    var_done = True
                Else
                    var_newList.Clear()
                    var_newList.AddRange(var_tempList)
                    var_dirList.AddRange(var_tempList)
                    var_tempList.Clear()
                End If
    
                If sw.ElapsedMilliseconds> 250 Then
                    sub_refreshLabel(String.Concat("Directories found: ", var_dirList.Count), statusWindow.obj_C_label.Text, statusWindow.obj_D_label.Text, statusWindow.obj_statPBar.Value)
                    sw.Restart
                End If
    
            Loop Until var_done
    
            'Directory search complete
            sub_editForms("Searching through directories", False, ProgressBarStyle.Continuous, 0, False)
            var_finishTime.Add(String.Concat("Directory mapping complete: ", CStr(DateTime.UtcNow)))
    
            'Start second task
            thread_fileSearch = New Threading.Thread(AddressOf task_findFiles)
            thread_fileSearch.Name = "fileSearch Thread"
            thread_fileSearch.IsBackground = True
            thread_fileSearch.Start()
        End Sub
    Or use a loop counter and update after so many loops. Would need trial and error to set the count value, so probably less reliable at ensuring the UI doesn't choke.

    Or use thread safe collections to store your retrieved Directories/Files, and a Forms Timer on the UI to update the UI with the Count properties of said collections. Sounds overcomplicated to me, but needs must where the Devil drives, and all that.

    Or investigate Tasks as mentioned earlier. Not sure they would provide any easier a solution to this particular problem, but then I've never used them .

  36. #36
    Frenzied Member
    Join Date
    Jul 2011
    Location
    UK
    Posts
    1,335

    Re: Multithreaded class

    Just in passing, I don't think there's any need for this part of your code:
    Code:
        'Start second task
        thread_fileSearch = New Threading.Thread(AddressOf task_findFiles)
        thread_fileSearch.Name = "fileSearch Thread"
        thread_fileSearch.IsBackground = True
        thread_fileSearch.Start()
    End Sub
    You are calling that from the end of the current worker thread . It spins up a second worker thread just before the current worker thread terminates.

    As you have it now, the two tasks run sequentially with no user intervention in regards to scheduling, so you might as well replace it all with a simple call to task_findFiles and continue on the current worker thread.

  37. #37

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Quote Originally Posted by Inferrd View Post
    Just in passing, I don't think there's any need for this part of your code:
    Code:
        'Start second task
        thread_fileSearch = New Threading.Thread(AddressOf task_findFiles)
        thread_fileSearch.Name = "fileSearch Thread"
        thread_fileSearch.IsBackground = True
        thread_fileSearch.Start()
    End Sub
    You are calling that from the end of the current worker thread . It spins up a second worker thread just before the current worker thread terminates.

    As you have it now, the two tasks run sequentially with no user intervention in regards to scheduling, so you might as well replace it all with a simple call to task_findFiles and continue on the current worker thread.
    Oh yeah, good point
    Thanks!

  38. #38

    Thread Starter
    Lively Member FireDust's Avatar
    Join Date
    Feb 2013
    Location
    Slovakia
    Posts
    112

    Re: Multithreaded class

    Thank you all for your responses, I think I learned what I needed!
    Marking as resolved.

    Also, while re-doing some parts of the code, I scrapped the overcomplicated task_mapoutDirectories sub, and came up something I think is worth sharing and that nobody suggested.

    Old code:
    Code:
        Private Sub task_MapoutDirectories()
    
            Dim var_newList As New List(Of String) : var_newList.AddRange(var_locList)
            Dim var_tempList As New List(Of String)
            Dim var_done As Boolean = False
    
            Do
                For Each var_location In var_newList
                    Try 'Permision exceptions can pop up
                        var_tempList.AddRange(Directory.EnumerateDirectories(var_location, "*", SearchOption.TopDirectoryOnly).ToList)
                    Catch ex As Exception
                    End Try
                Next
    
                If var_tempList.Count = 0 Then
                    var_done = True
                Else
                    var_newList.Clear()
                    var_newList.AddRange(var_tempList)
                    var_dirList.AddRange(var_tempList)
                    var_tempList.Clear()
                End If
            Loop Until var_done
        End Sub
    New code
    Code:
        Private Sub sub_mapout()
            Dim var_index As Integer = 0
            While var_dirList.Count <> var_index
                Try
                    var_dirList.AddRange(Directory.EnumerateDirectories(var_dirList.Item(var_index), "*", SearchOption.TopDirectoryOnly).ToList) : var_index += 1
                Catch ex As Exception : End Try
            End While
        End Sub
    I thought it was too simple, but It works perfectly so far

Tags for this Thread

Posting Permissions

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



Click Here to Expand Forum to Full Width