Results 1 to 10 of 10

Thread: Not a question, Just a Rant!

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Feb 2017
    Posts
    147

    Not a question, Just a Rant!

    so i have recently started coding and i wrote an application, was not the neatest code in the word but i got the application to work how i wanted it . all except that to UI would freeze while the code was running. so i thought i would google my problem, and i start reading about multithreading, im thinking Ahah!! this is the solution. so i start reading about multithreading, backgroundworkers, runningasync ,pending, parrellinvoke, dim thread as new thread ,invoke method this and that.

    so i added a couple backgroundworkers on my app . copied and pasted some code from ui thread into backgroundworkers. now im getting crossthreading exceptions , calling threads within threads and not making other methods threadsafe. i spend 6 hours googling and testing and manage to fix all these errors. and now somehow the chunk of code that worked fine before and had nothing to do with the mutlithreading is not working correctly. i have hit my head into my hands so many times today the wife asked what was wrong. i told her, "im contemplating running the code on a timer instead of a multithreaded loop" , her response is "do you want some of my apple danish".

    10 hours later my code is more broken then when i started trying to fix it, anyone else have these days where you are staring at the same damn piece of code thinking "Why are you doing that" or just me?

  2. #2
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,538

    Re: Not a question, Just a Rant!

    Of course we have. It's because we're always learning. Heck, I've been in the programming "industry" for 20+ professionally (even longer if you count the early hobby years) and I'm still learning new stuff all the time, sometimes to frustration. I was recently working with PHP to create a RESTful API ... and even after following the tutorial almost exactly, it wasn't working. Took me a couple days to figure it out (I had deviated from things by putting the code into a sub directory, the tutorial assumed using webroot).
    The key is knowing when to stop, take a break and come back to it.

    Now... to your multithreading issue... the most common problem is a simple lack of understanding just how it works and what the basic rules are. Once you get a handle of that, things will generally fall into place.
    Every app runs on at least one thread. This is the main thread, it is also the ONLY thread that can update the UI. This is usually why people get cross thread errors, when they try to update their UI from one of their alternate threads rather than the main UI Thread.
    That said, that doesn't mean that the alternate threads can't have a UI, but in order to do so, the UI has to be generate from THAT thread... but then the main thread wouldn't be able to use it either, nor would it be able to talk back to the main thread. Suffice it to say, unless there's a REALLY REALLY REALLY good reason to do so, all of the UI should be handled from the main thread only.
    Alternate threads are great for gathering data or doing large data manipulations in the background. They suck at displaying data. OK, so how do you get the data from the alt thread to the UI? It depends on how the thread was spun up. If you're using a BackGroundWorker (BGW) then it's done in the WorkCompleted event, which will be raised on the main UI Thread, where it's safe to update the UI.
    If you're not using the BGW, then you have to first check to see if the UI object you're about to update needs to be invoked on the main thread, and if so, call the .Invoke method on it, passing it what it needs in order to update. Huh? Wha? Yeah... sounds more complicated than it really is.
    Example: Let's say I want to update a textbox with some data from a second thread:
    Code:
    Delegate Sub SetText([Text] as String)
    
    Private Sub UpdateTB(NewText as String)
      
      If SomeTextBox.InvokeRequired Then
        Dim st as new SetText(AddressOf UpdateTB)
        Me.Invoke(st, NewText)
      else
        SomeTextBox.Text = NewText
    
    End Sub
    You can get a bit more detail from the following link.
    https://docs.microsoft.com/en-us/dot...forms-controls

    In short. the InvokeRequired indicates if it's on the main thread (false) or not (true). If it is on an alternate thread, then it uses a delegate to call back to the main thread and invokes the updateTB method. At that point, it's on the main thread, so the InvokeRequired is now false, and it can update the textbox safely.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  3. #3
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,764

    Re: Not a question, Just a Rant!

    So here is an example of an unresponsive UI

    Code:
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            CountUp(50000) 'try moving the form while this runs
        End Sub
    
        Private Sub CountUp(upTo As Integer)
            For x As Integer = 1 To upTo
                TextBox1.Text = x.ToString("n0")
                TextBox1.Refresh() 'to see the count
            Next
        End Sub
    A fix is to do the work, counting up, on a separate thread, and to only execute on the UI thread when the UI is being manipulated. In our contrived example the UI manipulation is most of the work.
    Code:
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            CountUp(50000) 'try moving the form while this runs
        End Sub
    
        Private Sub CountUp(upTo As Integer)
            Dim t As Task
            t = Task.Run(Sub()
                             For x As Integer = 1 To upTo
                                 'run on UI to update the textbox
                                 Me.Invoke(Sub()
                                               TextBox1.Text = x.ToString("n0")
                                               TextBox1.Refresh() 'to see the count
                                           End Sub)
                             Next
                         End Sub)
        End Sub
    As you can see the UI is responsive which can be good or bad. I say bad because with the UI responding you have to be aware that other form events, e.g. clicking button 1 again while the work is being performed can be done.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

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

    Re: Not a question, Just a Rant!

    Go take a walk.

    Apple Danish is an alternative, but you need to use it in moderation, and it's no substitute for a walk. Too much walking...and you'll be so far from home you'll need a ride, but you'll be fitter for it. Too much apple danish...well, you won't be fitter for it.

    That's not an entirely flippant reply, by the way. When I was starting out, I was amazed at how I would be stuck on a problem for hours, then drive home, and by the time I got home I had the answer. Later on, there was a time when I spent a couple days trying to solve a vexing problem. I had a solution, but I knew it was a horrible solution, so I went on a backpacking trip for a few days. As I was descending off some random mountain, I began thinking about the problem again, and had the solution in fifteen minutes, or less. It was an elegant solution, too. The rest of that day was one of epic misery worthy of a story, but I still remember how easily the right solution came to me after taking a couple days away from the problem.

    Later still, I found that this is actually fairly common practice. I've read more than one book or article where coders recommended taking walks when stuck on a problem. It's not about thinking it over. Your mind has the problem well in hand, and is plinking away at it, whether you are aware of it, or not. Just get out, let your mind wander, and when you're ready you'll come back to it with new insight.
    My usual boring signature: Nothing

  5. #5
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    41,930

    Re: Not a question, Just a Rant!

    I've had a lot of that in my career... when I already know the problem but I'm stuck, I avoid thinking about it any further.

    At some point (usually away from work) I'll suddenly have inspiration, and then I can solve it easily. Initially the inspiration would tend to happen just before bed (so I kept a notebook next to my bed), but thankfully it changed so it is generally at some point during the evening or day.

    Taking a break (food or exercise or something else) is a good way to go, but depending on the inspiration you need it may take a bit longer, so it can be good to work on something else for a few days.

  6. #6
    Smooth Moperator techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,538

    Re: Not a question, Just a Rant!

    You could also try the Rubber Duck Method - yes it's a thing... https://en.wikipedia.org/wiki/Rubber_duck_debugging
    I've found sometimes that simply trying to explain the problem to someone (or something) else can go a long way to breaking through. Worst case you get an apple danish. Best case you get an apple danish AND an answer to the problem.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  7. #7
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: Not a question, Just a Rant!

    Yes, this is a problem, and it's part of why it's a crime the .NET community didn't move to WPF and, particularly, the MVVM pattern it suggests.

    There is one "UI thread" in the application and it is sacred. Every moment you spend doing work on it is a moment the application can't refresh the screen and might look frozen. From this, it's easy to find tutorials and articles that tell you if you're doing something that might take a while, you have to use worker threads.

    But that "UI thread" is also the only thread in the application blessed with the ability to update the UI. So if you try to change something about a control from your worker threads, you get an exception while debugging or, worse, at runtime very strange things happen with no explanation.

    The best way to handle this problem is separation. If you only put "things that are UI updates" in a Form file, then you can't get messy. If you only put "worker thread stuff" in other files, it's easy to remember you have to be careful when they need to update the UI.

    We can visualize this like a fast-food restaurant. The cashier is the UI thread: they face the customer and have to interact with them. The kitchen staff are the worker threads. So with a Form as the cashier, we can talk about a transaction in a way that makes sense:

    1. The cashier collects the user's input and considers it "an order".
    2. The cashier prints a ticket and places it in the kitchen window.
    3. The kitchen staff receives the order and starts preparation.
    4. Some time later, the kitchen staff plates the order, places it in a window, and rings a bell.
    5. The cashier hears the bell and checks the window. It brings the food to the user.


    See how we have two different people, and they communicate via "tickets", "the window", and "a bell"? In our programs, that might be represented by "data structures" and "events". "The window" is a process called "marshalling" or "dispatching" but means "sending instructions from the worker thread to the UI thread safely".

    WPF and MVVM encourage you to write your programs in such a way that all of your UI code is in one class and your code that does work is in other classes. When you write code like this, you've already separated "UI thread" stuff from "worker thread" stuff so it's natural and intuitive to add the little bits that perform dispatching between them. Since the "worker" bits can never directly access the UI, it's easy to never mistakenly update the UI from the wrong thread.

    Windows Forms and "practically all tutorials" don't push for that practice. Most people decide "more classes are more complex, because tutorials never use them". So a lot of applications end up living entirely inside Form1.vb. This makes it very difficult to express which code is on worker threads and which code is on the UI thread. Since everything has direct access to the UI, it's very easy to get mixed up and update a control from the wrong thread.

    Doing it right in Windows Forms is actually a fairly large topic, but if you implement BackgroundWorkers in their own classes that's a big help. That gets you used to not directly updating the UI. I want to make an example, but it will probably be tomorrow before I can make an attempt, I've been really distracted at work lately.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  8. #8
    Super Moderator FunkyDexter's Avatar
    Join Date
    Apr 2005
    Location
    An obscure body in the SK system. The inhabitants call it Earth
    Posts
    7,902

    Re: Not a question, Just a Rant!

    I've considered padding the far wall of my home office. It would give the keyboard a fighting chance of survival when the two meet.
    The best argument against democracy is a five minute conversation with the average voter - Winston Churchill

    Hadoop actually sounds more like the way they greet each other in Yorkshire - Inferrd

  9. #9
    Fanatic Member 2kaud's Avatar
    Join Date
    May 2014
    Location
    England
    Posts
    1,000

    Re: Not a question, Just a Rant!

    so i added a couple backgroundworkers on my app .
    Apart from all the excellent advice already given, may I suggest that instead of trying something 'new' in your application, you write some small test programs first to try it out and get the hang of it. Then when you're happy with the new concept etc then and only then apply it to your main program.
    All advice is offered in good faith only. You are ultimately responsible for the effects of your programs and the integrity of the machines they run on. Anything I post, code snippets, advice, etc is licensed as Public Domain https://creativecommons.org/publicdomain/zero/1.0/

    C++23 Compiler: Microsoft VS2022 (17.6.5)

  10. #10

    Thread Starter
    Addicted Member
    Join Date
    Feb 2017
    Posts
    147

    Re: Not a question, Just a Rant!

    great advice guys .i took the dog for a walk, and while i was walking i was thinking about the code snippet Technome posted, and it just clicked. i think half my problems come from writing code in modules and not classes. i don't seem to be able to update uithread with calls from code within a module
    Last edited by Beanoid; Apr 28th, 2018 at 07:00 AM.

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