Microsoft Developer Network > 포럼 홈 > .NET Base Class Library > How to Pause or sleep the background worker thread
질문하기질문하기
 

답변됨How to Pause or sleep the background worker thread

  • 2006년 5월 3일 수요일 오후 12:12mks033 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    Posted - 05/03/2006 :  07:03:30 AM    
    I m using the background worker thread to done the task. i m using this because my application required the update of the GUI based on the status of work so background worker thread will be helpful to me. Now the issue is that i have one button of pause/resume to pause/resume the working of this background worker thread. i haven't found any function of this component to perform this task.
    Kindly help me in this regard
    Thanks in advance

답변

  • 2006년 5월 3일 수요일 오후 6:50Vladimir Zinchenko 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     답변됨

    Well, seems like the class itself doesn't provide this behavior, however there's a workaround.

    You can monitor some class member inside worker's procedure and call Sleep from within the worker. I think something like this should work fine:

    class MyWorkerClass
    {
      ...
      volatile bool m_bPaused  = false;
      // Public property to control worker execution
      public bool Paused
      {
        get
        {
          return m_bPaused;
        }

        set
        {
          m_bPaused = value;
        }
      }

      long ThreadFunc (BackgroundWorker worker, DoWorkEventArgs e)
      {
        ...
        // While Paused property set to true
        while (m_bPaused)
        {
          // Pause execution for some time
          System.Threading.Thread.Sleep (100);

          // Maybe you would like to check if the worker cancelled
          if (worker.CancellationPending)
          {
            e.Cancel = true;
            // return from method here
          }
        }
      }
      ...
    }

     

모든 응답

  • 2006년 5월 3일 수요일 오후 4:23ShellShock 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    System.Threading.Thread.Sleep method.
  • 2006년 5월 3일 수요일 오후 4:29mks033 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     

    i have checked this out but its stops my application. At backend the background worker thread continue its execution. may be i m in fault if i m then kindly let me know so that i will recheck this case once again As far as i know i have tested that.

    Thanks

  • 2006년 5월 3일 수요일 오후 6:18Vladimir Zinchenko 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    Use Thread.Suspend to pause thread execution and then Thread.Resume to make it running afterwards.
  • 2006년 5월 3일 수요일 오후 6:25mks033 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    yes these function can be used but these functions belong to the namespace System.Threading.Thread while Background worker belongs to the "System.ComponentModel.BackgroundWorker" and there is no such function in that.
  • 2006년 5월 3일 수요일 오후 6:50Vladimir Zinchenko 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     답변됨

    Well, seems like the class itself doesn't provide this behavior, however there's a workaround.

    You can monitor some class member inside worker's procedure and call Sleep from within the worker. I think something like this should work fine:

    class MyWorkerClass
    {
      ...
      volatile bool m_bPaused  = false;
      // Public property to control worker execution
      public bool Paused
      {
        get
        {
          return m_bPaused;
        }

        set
        {
          m_bPaused = value;
        }
      }

      long ThreadFunc (BackgroundWorker worker, DoWorkEventArgs e)
      {
        ...
        // While Paused property set to true
        while (m_bPaused)
        {
          // Pause execution for some time
          System.Threading.Thread.Sleep (100);

          // Maybe you would like to check if the worker cancelled
          if (worker.CancellationPending)
          {
            e.Cancel = true;
            // return from method here
          }
        }
      }
      ...
    }

     

  • 2007년 4월 26일 목요일 오전 8:48Lex Li 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    I have been working on such a topic for a while and now I find a way (but may contains bugs).

            void Button1Click(object sender, EventArgs e)
            {
                backgroundWorker1.RunWorkerAsync();
            }
           
            void Button2Click(object sender, EventArgs e)
            {
                if (button2.Text == "Pause") {
                    Active = false;
                    button2.Text = "Resume";
                } else {
                    Active = true;
                    button2.Text = "Pause";
                }
            }
         
            void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
            {
                int i = 1000;
                while(i > 0) {
                    if (Active) {
                        i--;
                        backgroundWorker1.ReportProgress(i);
                        System.Threading.Thread.Sleep(10);
                    }
                }
            }
           
            private bool Active = true;
           
            void BackgroundWorker1ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
            {
                richTextBox1.AppendText(e.ProgressPercentage.ToString() + Environment.NewLine);
            }
           
            void BackgroundWorker1RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
            {
                MessageBox.Show("Completed.");
            }
    In the sample code, a Active flag is used. I know this may not suit your need, but it meets my requirements well.
  • 2007년 4월 26일 목요일 오후 2:14nobugzMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    To do this without sleeping and unnecessary delays, use a ManualResetEvent.  Call Set() to allow the worker to execute, Reset() to block it.  In the worker, call WaitOne() to block.
  • 2007년 4월 27일 금요일 오전 12:50Lex Li 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    Yes, yes, it is a better way to use a MenualResetEvent. I have updated my sample code like this:

            void Button1Click(object sender, EventArgs e)
            {
                backgroundWorker1.RunWorkerAsync();
            }
           
            void Button2Click(object sender, EventArgs e)
            {
                if (button2.Text == "Pause") {
                    locker.Reset();
                    button2.Text = "Resume";
                } else {
                    locker.Set();
                    button2.Text = "Pause";
                }
            }
         
            void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
            {
                int i = 1000;
                while(i > 0) {
                       locker.WaitOne();
                        i--;
                        backgroundWorker1.ReportProgress(i);
                        System.Threading.Thread.Sleep(10);
                }
            }
           
            private ManualResetEvent locker = new ManualResetEvent(true);
           
            void BackgroundWorker1ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
            {
                richTextBox1.AppendText(e.ProgressPercentage.ToString() + Environment.NewLine);
            }
           
            void BackgroundWorker1RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
            {
                MessageBox.Show("Completed.");
            }

  • 2007년 4월 27일 금요일 오전 2:55Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
     ShellShock wrote:
    System.Threading.Thread.Sleep method.
    Thread.Sleep is poor design.
  • 2007년 4월 27일 금요일 오전 2:59Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
     Vladimir Zinchenko wrote:
    Use Thread.Suspend to pause thread execution and then Thread.Resume to make it running afterwards.
    Thread.Suspend is obsolete.  Plus, you can't externally get the Thread object used by the background worker.  BackgroundWorker uses a thread pool thread, if you could use Thread.Suspend it would be really bad idea.
  • 2007년 4월 27일 금요일 오전 8:16decyclone 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    Use of Thread.Sleep() is the last way for pausing a thread when the time of pause is not known. You will have to loop infinite with a Sleep with some milliseconds and check if it can proceed. If you use Thread.Suspend / Resume, you will get warning saying that they are marked as obsolete and use Monitor or Waithandles instead. The method that is executed asynchronously should check frequently for a flag or should try to acquire a handle, every few loop cycles or every few statements.
  • 2007년 4월 27일 금요일 오전 11:18Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     

    "pausing" a background worker thread is a waste of a thread pool thread.  There's a limited number of thread pool threads per process and they are also used by the framework; if you "pause" thread pool threads you run the risk of staving the framework of threads and you'll create bigger problems.

     

    Any background thread should do what it needs to do and terminate as quickly as possible.  I would suggest if the operation is "pausable" then it's also cancel-able; in which case simply offer the ability to cancel instead of pause and exit the background worker until the user requests the operation start again.

  • 2007년 4월 28일 토요일 오전 4:39CommonGenius.com중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     

     Peter Ritchie wrote:

    I would suggest if the operation is "pausable" then it's also cancel-able...


    I hardly think this assumption can be made without knowing more about the circumstances of the application. However, implementation details don't have to match what's happening from the user's perspective. By that I mean, when the user wants to "pause" the background worker, you should save the state of the process that the background worker is performing, then cancel the worker, releasing its thread back to the thread pool. When the user "resumes", create a new background worker using the saved state, and continue processing.
  • 2007년 4월 28일 토요일 오후 1:37Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
     CommonGenius.com wrote:

     Peter Ritchie wrote:

    I would suggest if the operation is "pausable" then it's also cancel-able...


    I hardly think this assumption can be made without knowing more about the circumstances of the application. However, implementation details don't have to match what's happening from the user's perspective. By that I mean, when the user wants to "pause" the background worker, you should save the state of the process that the background worker is performing, then cancel the worker, releasing its thread back to the thread pool. When the user "resumes", create a new background worker using the saved state, and continue processing.

    Since Thread.Suspend is impossible with a BackgroundWorker the logic in the DoWork requires some sort of flag to "pause" it. To cancel the operation the variables used in whatever iterative logic would have to be cached, yes; but it's still cancel-able.  I would assert anything that is truly pausable is also cancel-able (the qualification being that Thread.Suspend should not be used at all). I could have qualified my statement as "...if you think the operation is 'pausable' then..." which, yes, was assumed in the fact the OP implied no code had yet been written.

     

    But, yes, in the context of "pause" in the user interface, what is performed under the covers is an implementation detail: it could be a call in the DoWork event handler to a WaitOne method to put that thread pool thread into a wait state (not recommended) or it could mean saving the state required to restart the BackgroundWorker, canceling it, and letting it get collected (recommended) to be recreated when unpaused.

  • 2009년 2월 23일 월요일 오전 2:17Ewart 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
            
    Peter Ritchie said:

    Since Thread.Suspend is impossible with a BackgroundWorker the logic in the DoWork

    What to you mean by this Peter?  It seems to work fine for me.
     
          protected override void OnDoWork(DoWorkEventArgs e)
          {
             // Sleep for given wait time in milliseconds
             System.Threading.Thread.Sleep(10000);
              ..
         }

    My design requirement is for my tray app to wait 6 minutes after loading (normally system boot-up), then check for updates.  If updates are available the user gets a dialog prompting for download now or not.

    I originally used this line from my tasktray Form constructor, with the first line calling Sleep to sleep the thread for a few minutes.
    ThreadPool.QueueUserWorkItem(new WaitCallback(AutoUpdate.CheckForUpdates), new ThreadParams(this, C_DELAY_UPDATE))

    It worked fine however I was using MessageBox.ShowDialog() within that thread to prompt for updates and was concerned that perhaps MessageBox.ShowDialog()  might not automatically marshall to the UI thread or would cause some errors in that regard so I thought I'd try using BackgroundWorker instead.

    The BW worker runs just as fine as the previous ;) and should not have any UI thread issues, however I guess upon reflection I wanted to be sure firing sleeping the thread was an OK approach, which led me here.  In this context are there any issues with this approach? 

    I can just as easily create a timer with to fire off my upgrade check, which also works well, but I guess I'd like to understand a little more about this from an expert:
    tmrUpgrade = new System.Threading.Timer(cbCheckUpgrade, null, C_DELAY_UPDATE, System.Threading.Timeout.Infinite);

    Regards
      Ewart.
  • 2009년 2월 23일 월요일 오후 2:52Peter RitchieMVP, 중재자사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     
    Thread.Sleep is possible from a BackgroundWorker thread because you can call that method within the DoWork event handler.  Thread.Suspend isn't possible because you need a reference to a thread object and the thread object used by the BackgroundWorker is a thread-pool thread.  By calling Thread.Suspend on another thread, you have no way of knowing is the thread you're suspending is still the thread being used by your BackgroundWorker--it could have been reused by something else, even by the framework.

    I don't recommend using Thread.Sleep as a timing mechanism to execute code.  In your case where you'd want to pause for 6 minutes; using sleep would mean you can't exit that thread until Sleep returns; which means your application can't reliably exit until that thread exits.  Yes, there's ways around that; but they all deal with abnormal termination of the thread which can lead to other problems.

    If you want to perform some sort of action periodically; a timer is your best bet.  If you know you're only going to have a single elapsed event occurring at a time, or you're only going to have one timer, and you want it to do something with the UI the System.Windows.Forms.Timer class would be a better choice.  It's Tick event (equivalent to the System.Thread.Timer.Elapsed event) runs on the GUI thread.

    http://www.peterRitchie.com/blog
  • 2009년 2월 23일 월요일 오후 6:53Ewart 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     제안된 답변
    All makes perfect sense to me - I shall continue using the timer but change it to a Forms timer from a threading one - thanks Peter.
    • 답변으로 제안됨mike-webdude 2009년 7월 15일 수요일 오후 4:27
    •  
  • 2009년 7월 15일 수요일 오후 4:36mike-webdude 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     제안된 답변코드 있음
    I tried something a bit different take a look:  My AutoResetEvent will allow me to stop only my BackgroundWorker and not the Applications Thread, I use the WaitCancellationPending to see if my Thread is currently Awaiting Cancellation Pending, this was helpful for using a MessageBox that asked the user if they were sure they wanted to cancel the current operation, and put the Thread in a holding state until it recieves a response.  The next step would be to allow the application to override the action if it does not recieves a response in a reasonable amount of time.  We'll see.

     

    public class MyBackgroundWorker : BackgroundWorker
    {
      private AutoResetEvent _resetEvent = new AutoResetEvent(false);<br/>
      public bool WaitCancellationPending { get; set; }
      public void WaitOne()
      {
        _resetEvent.WaitOne();
      }
      public void Set()
      {
        _resetEvent.Set();
      }
    }
    

     

     

     

    • 편집됨mike-webdude 2009년 7월 15일 수요일 오후 4:38
    • 답변으로 제안됨mike-webdude 2009년 7월 15일 수요일 오후 4:42
    • 편집됨mike-webdude 2009년 7월 15일 수요일 오후 4:39
    • 편집됨mike-webdude 2009년 7월 15일 수요일 오후 4:40
    •  
  • 2009년 11월 7일 토요일 오전 10:07fasdffasdfa 사용자 메달사용자 메달사용자 메달사용자 메달사용자 메달
     코드 있음
    public class MyBackgroundWorker : BackgroundWorker
    {
      private AutoResetEvent _resetEvent = new AutoResetEvent(false);
      public bool WaitCancellationPending { get; set; }
      public void WaitOne()
      {
        _resetEvent.WaitOne();
      }
      public void Set()
      {
        _resetEvent.Set();
      }
    }
    
    



    me likey.