Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. User Feedback while main thread is busy

User Feedback while main thread is busy

Scheduled Pinned Locked Moved C#
cssbeta-testinghelpcode-review
18 Posts 6 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Mycroft Holmes

    I have 2 common operations that I want to perform from a thread, update a textbox or label (typically with the name of a completed process) or increment a progress bar so testing to the progress bar and an else would meet 90% of my needs. Where I need to update a listview, which I often need to, I would use a specific delegated method.

    L Offline
    L Offline
    Luc Pattyn
    wrote on last edited by
    #7

    If I understand you correctly, I would create a couple of global methods then, one for SetText (taking a Control, as all Controls have a Text property; and a string), one for SetProgress (taking a ProgressBar, not a Control, and a number), etc. That's a bit more code (statically), however it does not perform any unnecessary type checking (with as and is). BTW: especially for progress bars, make sure you don't update progress at a ridiculous precision; if you're not careful each iteration would cause a thread switch and possibly just confirm the previous version (assuming you are using a small range of values, say percentages). So it may be worthwhile to "cache" the latest value and short-circuit when there isn't a real change. (I should add this to my article) FWIW: this holds true as well when using BackgroundWorker.ReportProgress(), however I haven't seen this kind of warnings in MSDN yet. :)

    Luc Pattyn [Forum Guidelines] [My Articles]


    I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


    M 1 Reply Last reply
    0
    • L Luc Pattyn

      If I understand you correctly, I would create a couple of global methods then, one for SetText (taking a Control, as all Controls have a Text property; and a string), one for SetProgress (taking a ProgressBar, not a Control, and a number), etc. That's a bit more code (statically), however it does not perform any unnecessary type checking (with as and is). BTW: especially for progress bars, make sure you don't update progress at a ridiculous precision; if you're not careful each iteration would cause a thread switch and possibly just confirm the previous version (assuming you are using a small range of values, say percentages). So it may be worthwhile to "cache" the latest value and short-circuit when there isn't a real change. (I should add this to my article) FWIW: this holds true as well when using BackgroundWorker.ReportProgress(), however I haven't seen this kind of warnings in MSDN yet. :)

      Luc Pattyn [Forum Guidelines] [My Articles]


      I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


      M Offline
      M Offline
      Mycroft Holmes
      wrote on last edited by
      #8

      Thinking about it I had already come to the 2 method solution and thinking more I will probably create a generic listview updater as well. I always do the same thing when processing a list of stored procs, uncheck the item, add the record count and time to sub items.

      Luc Pattyn wrote:

      don't update progress at a ridiculous precision

      Been there, I regularly process 100k+ result sets, you very quickly learn to lighten the load when using a progress bar:-D. I think it should be sooooo obvious the warning would be a little redundant.

      L 1 Reply Last reply
      0
      • M Mycroft Holmes

        Thinking about it I had already come to the 2 method solution and thinking more I will probably create a generic listview updater as well. I always do the same thing when processing a list of stored procs, uncheck the item, add the record count and time to sub items.

        Luc Pattyn wrote:

        don't update progress at a ridiculous precision

        Been there, I regularly process 100k+ result sets, you very quickly learn to lighten the load when using a progress bar:-D. I think it should be sooooo obvious the warning would be a little redundant.

        L Offline
        L Offline
        Luc Pattyn
        wrote on last edited by
        #9

        Mycroft Holmes wrote:

        the warning would be a little redundant

        Now I disagree. Too often I've seen code (not mine!) that would run say 10 times faster by commenting out the progress bar, sometimes making it so fast a progress bar doesn't make much sense any more. Remember, with the Invoke in place, a thread switch could easily dominate the bulk of the loop code. Seems some people lack a feeling for normal and abnormal speed. [EDIT]The article has been updated.[/EDIT] :)

        Luc Pattyn [Forum Guidelines] [My Articles]


        I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


        N 1 Reply Last reply
        0
        • L Luc Pattyn

          Mycroft Holmes wrote:

          the warning would be a little redundant

          Now I disagree. Too often I've seen code (not mine!) that would run say 10 times faster by commenting out the progress bar, sometimes making it so fast a progress bar doesn't make much sense any more. Remember, with the Invoke in place, a thread switch could easily dominate the bulk of the loop code. Seems some people lack a feeling for normal and abnormal speed. [EDIT]The article has been updated.[/EDIT] :)

          Luc Pattyn [Forum Guidelines] [My Articles]


          I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


          N Offline
          N Offline
          N a v a n e e t h
          wrote on last edited by
          #10

          Totally agree with you. You don't have to update the progress bar unnecessarily. :thumbsup: BTW, I used to use BeginInvoke than Invoke when I need to update the controls. This executes as an asynchronous call and won't block the worker thread until progress bar is updated. :)

          Best wishes, Navaneeth

          L 1 Reply Last reply
          0
          • N N a v a n e e t h

            Totally agree with you. You don't have to update the progress bar unnecessarily. :thumbsup: BTW, I used to use BeginInvoke than Invoke when I need to update the controls. This executes as an asynchronous call and won't block the worker thread until progress bar is updated. :)

            Best wishes, Navaneeth

            L Offline
            L Offline
            Luc Pattyn
            wrote on last edited by
            #11

            yes, you have told me that before; I did not react on it then, other than start thinking about it. So I did, and I decided not to change my ways. Of course I do agree you can use BeginInvoke, and it has the advantage of not blocking the worker. However it does not really fit my "canonical form", where a single method can be called from the GUI thread as well as any other thread. If I were to use BeginInvoke there, it would be a synchronous method when called from the GUI thread, and asynchronous when called from another thread, which would probably be very confusing. The same point, in other words, is: I prefer asynchronous operations to be obvious from their name, so "Begin" or "Async" should be part of the method name, which then wouldn't be possible in my canonical form. Another, minor, issue might be: I do not like the idea of a lot of GUI work piling up somewhere, as this (1) would consume memory nobody is aware of, and (2) will consume a lot of CPU cycles even after the worker has finished. Of course, if the net result would be fewer thread switches (I should perform an experiment), then it would have its merits. And finally, I just added to the article a remark about avoiding frequent progress bar updates; having them synchronous should make it easier for the programmer to notice something is going wrong in that area. If and when I do some experiments on this, I'll let you know the results. :)

            Luc Pattyn [Forum Guidelines] [My Articles]


            I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


            N 1 Reply Last reply
            0
            • L Lost User

              I want simple way to give feedback to the user while the main thread is busy. In the main thread I want something like ProgressBox prog = new ProgressBox(); prog.Show(); for ( int i=0 ; ; i++ ) { ... prog.NumProcessed = i; } prog.Close(); The problem is that the ProgressBox freezes after a couple of seconds and updates only once after the loop is finished. Ok, it has something to do with Threading. However, I have tried to find a solution, but all seems to come down to performing the work(the for loop here) in another thread. But this is not what I want. I want the work to be performed in the main thread for simplicity if the main thread has to wait anyway! The ProgressBox can run in separate thread, I could not care less. But I need a generic solution for feedback to the user instead of a "looks like a crash". I might just be too stupid, but anyone already stumbled across a solution to this. regards Rolf

              L Offline
              L Offline
              Lost User
              wrote on last edited by
              #12

              The easiest way is to write Application.DoEvents() inside the for loop. This will allow the application to process its message queue and update the Progress bar, but the downside is that your loop can become slower by upto 10 times (or even more). Using a BackgroundWorker is the most appropriate choice for background activities. It runs your code in a separate thread which allows your main thread to process other messages and update the GUI.

              L 1 Reply Last reply
              0
              • L Luc Pattyn

                yes, you have told me that before; I did not react on it then, other than start thinking about it. So I did, and I decided not to change my ways. Of course I do agree you can use BeginInvoke, and it has the advantage of not blocking the worker. However it does not really fit my "canonical form", where a single method can be called from the GUI thread as well as any other thread. If I were to use BeginInvoke there, it would be a synchronous method when called from the GUI thread, and asynchronous when called from another thread, which would probably be very confusing. The same point, in other words, is: I prefer asynchronous operations to be obvious from their name, so "Begin" or "Async" should be part of the method name, which then wouldn't be possible in my canonical form. Another, minor, issue might be: I do not like the idea of a lot of GUI work piling up somewhere, as this (1) would consume memory nobody is aware of, and (2) will consume a lot of CPU cycles even after the worker has finished. Of course, if the net result would be fewer thread switches (I should perform an experiment), then it would have its merits. And finally, I just added to the article a remark about avoiding frequent progress bar updates; having them synchronous should make it easier for the programmer to notice something is going wrong in that area. If and when I do some experiments on this, I'll let you know the results. :)

                Luc Pattyn [Forum Guidelines] [My Articles]


                I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


                N Offline
                N Offline
                N a v a n e e t h
                wrote on last edited by
                #13

                Luc Pattyn wrote:

                Of course, if the net result would be fewer thread switches (I should perform an experiment), then it would have its merits.

                I don't think BeginInvoke has this advantage over Invoke as both are doing the same API call internally. All it does extra is to run the method in a separate thread to get asynchronous nature.

                Luc Pattyn wrote:

                If and when I do some experiments on this, I'll let you know the results.

                Thanks. I will be very much interested to hear from you. :)

                Best wishes, Navaneeth

                1 Reply Last reply
                0
                • L Lost User

                  The easiest way is to write Application.DoEvents() inside the for loop. This will allow the application to process its message queue and update the Progress bar, but the downside is that your loop can become slower by upto 10 times (or even more). Using a BackgroundWorker is the most appropriate choice for background activities. It runs your code in a separate thread which allows your main thread to process other messages and update the GUI.

                  L Offline
                  L Offline
                  Luc Pattyn
                  wrote on last edited by
                  #14

                  calling Application.DoEvents() is dangerous; for one it risks a StackOverflowException. I do not recommend it unless I am sure the programmer knows the internals of the Windows messaging system (in which case he probably does not need any advice and wouldn't have asked the question at all). :)

                  Luc Pattyn [Forum Guidelines] [My Articles]


                  I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


                  L N 2 Replies Last reply
                  0
                  • L Luc Pattyn

                    calling Application.DoEvents() is dangerous; for one it risks a StackOverflowException. I do not recommend it unless I am sure the programmer knows the internals of the Windows messaging system (in which case he probably does not need any advice and wouldn't have asked the question at all). :)

                    Luc Pattyn [Forum Guidelines] [My Articles]


                    I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


                    L Offline
                    L Offline
                    Lost User
                    wrote on last edited by
                    #15

                    Yes, I know that. I just wanted to give an heads up on why his GUI remained unchanged when his main thread was processing a loop.

                    1 Reply Last reply
                    0
                    • L Luc Pattyn

                      calling Application.DoEvents() is dangerous; for one it risks a StackOverflowException. I do not recommend it unless I am sure the programmer knows the internals of the Windows messaging system (in which case he probably does not need any advice and wouldn't have asked the question at all). :)

                      Luc Pattyn [Forum Guidelines] [My Articles]


                      I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


                      N Offline
                      N Offline
                      Natza Mitzi
                      wrote on last edited by
                      #16

                      I would like to add that when DoEvents is called, your GUI becomes "alive" in the middle of the process and your user can click buttons and create unstable UI states. It also seems like calling DoEvents inside a loop may decrease performance to the ground depending on your loop code. When using threading and UI make sure to use Invoke and BeginInvoke properly to avoid dead locks or racing/overflowing

                      Natza Mitzi Analysis Studio Statistical Analysis Software

                      L 1 Reply Last reply
                      0
                      • N Natza Mitzi

                        I would like to add that when DoEvents is called, your GUI becomes "alive" in the middle of the process and your user can click buttons and create unstable UI states. It also seems like calling DoEvents inside a loop may decrease performance to the ground depending on your loop code. When using threading and UI make sure to use Invoke and BeginInvoke properly to avoid dead locks or racing/overflowing

                        Natza Mitzi Analysis Studio Statistical Analysis Software

                        L Offline
                        L Offline
                        Luc Pattyn
                        wrote on last edited by
                        #17

                        Wrong target, I know these things and am reluctant to use DoEvents(). :)

                        Luc Pattyn [Forum Guidelines] [My Articles]


                        I only read code that is properly indented, and rendered in a non-proportional font; hint: use PRE tags in forum messages


                        1 Reply Last reply
                        0
                        • L Lost User

                          I want simple way to give feedback to the user while the main thread is busy. In the main thread I want something like ProgressBox prog = new ProgressBox(); prog.Show(); for ( int i=0 ; ; i++ ) { ... prog.NumProcessed = i; } prog.Close(); The problem is that the ProgressBox freezes after a couple of seconds and updates only once after the loop is finished. Ok, it has something to do with Threading. However, I have tried to find a solution, but all seems to come down to performing the work(the for loop here) in another thread. But this is not what I want. I want the work to be performed in the main thread for simplicity if the main thread has to wait anyway! The ProgressBox can run in separate thread, I could not care less. But I need a generic solution for feedback to the user instead of a "looks like a crash". I might just be too stupid, but anyone already stumbled across a solution to this. regards Rolf

                          L Offline
                          L Offline
                          Lost User
                          wrote on last edited by
                          #18

                          Hi! I understand now, that I was a bit naive about my question. After a bit of thought, I guess using the BackgroundWorker is indeed the best solution. In future, I will definitely give more thought to the general division of GUI and program from the start, not only in the sense of separating the code (what I already did in not using GUI elements to store data etc.) but also in the sense of separating code execution to avoid such deadlocks. Although that means having some kind of state-machine in the GUI to handle the asynchronous working (in most simple cases just 2 states, busy and idle). A shame that C#/Visual Studion/Runtime lib do not clearly enforce such a separation. Or is there a framework to enforce(!), not just enable, this ? Thanks for the discussion, Rolf

                          1 Reply Last reply
                          0
                          Reply
                          • Reply as topic
                          Log in to reply
                          • Oldest to Newest
                          • Newest to Oldest
                          • Most Votes


                          • Login

                          • Don't have an account? Register

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • World
                          • Users
                          • Groups