How to add the function of pause / resume work in the application?

I am writing an application where most of the work is done by background threads (10-500 threads).

I would like to add pause / resume functionality.

Before that, you could do this with Thread.Suspend and Thread.Resume. But now these functions are considered obsolete.

Is there anything that would allow me to do the same with equal ease?

I am writing software in C #

+6
source share
4 answers

By writing a high-performance crawler in C #, I can say that some permissions that explicitly control tens or hundreds of threads are not the best way. It can be done (I did it), but as a last resort it hurts.

That said.,.

If your application is written as I think, then each thread does something like this:

while (!Shutdown) { // get next url to crawl from somewhere // download the data from that url // do something with the data } 

Pausing threads between downloads is quite simple. I would suggest creating two instances of ManualResetEvent : one to continue and one to turn off. This is static so that all crawler threads can access them:

 static ManualResetEvent ShutdownEvent = new ManualResetEvent(false); static ManualResetEvent ContinueEvent = new ManualResetEvent(true); 

Then each thread uses WaitAny in a loop:

 WaitHandle[] handles = new WaitHandle[] { ShutdownEvent, ContinueEvent }; while (true) { int handle = WaitHandle.WaitAny(handles); // wait for one of the events if (handle == -1 || handle >= handles.Length) { throw new ApplicationException(); } if (handles[handle] = ShutdownEvent) break; // shutdown was signaled if (handles[handle] == ContinueEvent) { // download the next page and do something with the data } } 

Note that when I defined the handles array, I first specified the ShutdownEvent . The reason is that if multiple elements are WaitAny , WaitAny returns the lowest index corresponding to the signal object. If the array was filled in a different order, you cannot exit without first pausing.

Now, if you want the threads to shut down, call ShutdownEvent.Set . And if you want the threads to pause, call ContinueEvent.Reset When you want the threads to resume, call ContinueEvent.Set .

A pause in the middle of loading is a bit more complicated. This can be done, but the problem is if you pause the server timeout too long. And then you have to restart the download from the beginning, or if the server and your code support it, restart the download from where you left off. Any option is quite painful, so I would not suggest trying to pause in the middle of the download.

+2
source

What does your application do?

500 threads is too much - this is 1/2 GB of memory designed only for stacks. And then you have all the context switching.

It’s good that you want to get rid of Suspend and Resume calls, but I suggest you take a look at your architecture first - can you move on to APM methods (BeginXXX / EndXXX)?

+2
source

As a warning about what I'm going to say: it is unclear what your application is doing; There are many easy ways to use thread pool threads, such as TPL, background workers, etc.

However, if you have threads that you created (rather than threadpool) and you want them to communicate, use Monitor.Wait and Monitor.Pulse with a logical block condition.

eg:

  bool _isPaused; void DoWork() { while (true) { lock (_locker) { while (_isPaused) Monitor.Wait(_locker); // your worker code here } } } // void UnPause() { lock (_locker) { _isPaused=false; Monitor.PulseAll(_locker); } } 
+2
source

Not really. Suspend / Resume are really simple and work fine until they crash your application, for example. by pausing the thread in which the memory manager or file system is blocked: (

The usual, slightly more complicated approach is to find somewhere in your threads where you can wait. In your case, I assume that most threads are usually blocked on any I / O call anyway, and therefore they are “suspended”, so a good place to force the suspension “right” after reading is to catch those threads, reading will return.

You can make an actual suspension by checking the global logical flag isRunning suggested by @Andrey, and if you want to suspend, block the global ManualResetEvent. To pause, clear the event and then the flag. To resume, set the flag and then the event.

If using global variables makes you feel nauseous, you can pass to ctor a common instance of some class that contains flags, events, and methods suspend (), 'resume ()' and 'checkForSuspend ().

Rgds, Martin

+1
source

Source: https://habr.com/ru/post/898984/


All Articles