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.
  • L Offline
    L Offline
    Lost User
    wrote on last edited by
    #1

    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 D L 4 Replies 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
      Luc Pattyn
      wrote on last edited by
      #2

      Hi, You MUST do the long operation in a separate thread (a Thread object, a ThreadPool thread, a BackgroundWorker, whatever) and NOT on the main thread as that needs to be available at all times to keep the GUI alive (and update all forms/controls, including progress bars). If the long operation needs access to the GUI you may want to read this[^], unless a simple BackgroundWorker suffices. :)

      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


      modified on Wednesday, November 25, 2009 5:00 PM

      M 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

        D Offline
        D Offline
        DaveyM69
        wrote on last edited by
        #3

        Luc is correct, the right way to do this is to do the operation in another thread and make that thread report back the progress to the GUI thread so it can update progress bars or whatever. This is exactly what the BackgroundWorker component was designed for.

        Dave
        BTW, in software, hope and pray is not a viable strategy. (Luc Pattyn)
        Why are you using VB6? Do you hate yourself? (Christian Graus)

        1 Reply Last reply
        0
        • L Luc Pattyn

          Hi, You MUST do the long operation in a separate thread (a Thread object, a ThreadPool thread, a BackgroundWorker, whatever) and NOT on the main thread as that needs to be available at all times to keep the GUI alive (and update all forms/controls, including progress bars). If the long operation needs access to the GUI you may want to read this[^], unless a simple BackgroundWorker suffices. :)

          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


          modified on Wednesday, November 25, 2009 5:00 PM

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

          Luc If I take your canonical pattern and shove it into a static utilities class (possibly extending it by checking the typeof control) I presume this would act as a global thread safe control updater (only called when using a second thread naturally).

          L 1 Reply Last reply
          0
          • M Mycroft Holmes

            Luc If I take your canonical pattern and shove it into a static utilities class (possibly extending it by checking the typeof control) I presume this would act as a global thread safe control updater (only called when using a second thread naturally).

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

            Yes you could do that; I never used it that way, most often I don't even pass the Control, I tend to create one or a few dedicated methods for each control (as there aren't that many) within the Form class, but there probably isn't a good reason for doing so, except for providing a more functional name to the method. Note some actions may need more parameters, say setting the n-th item in a listbox. And then there is the performance issue when you need to update a lot, it is wise to pass all data and cross the thread barrier only once. :)

            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

              Yes you could do that; I never used it that way, most often I don't even pass the Control, I tend to create one or a few dedicated methods for each control (as there aren't that many) within the Form class, but there probably isn't a good reason for doing so, except for providing a more functional name to the method. Note some actions may need more parameters, say setting the n-th item in a listbox. And then there is the performance issue when you need to update a lot, it is wise to pass all data and cross the thread barrier only once. :)

              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
              #6

              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 1 Reply Last reply
              0
              • 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