简体   繁体   English

如何向应用程序添加暂停/恢复功能?

[英]How to add pause/resume functionality to an application?

I'm writing an app, where most of the work is done by background threads (10 - 500 threads). 我正在编写一个应用程序,其中大部分工作都是由后台线程完成的(10 - 500个线程)。

I'd like to add pause/resume functionality. 我想添加暂停/恢复功能。

Before, you could do that with Thread.Suspend and Thread.Resume. 之前,你可以使用Thread.Suspend和Thread.Resume来做到这一点。 But those functions are considered obsolete now. 但是这些功能现在已经过时了。

Is there anything that would let me do the same with equal ease? 还有什么可以让我同样轻松地做同样的事吗?

I'm writing the software in c# 我正在用c#编写软件

What does your app do? 你的应用程序做什么?

500 threads is far too many - that's 1/2 GB of committed memory just for the stacks. 500个线程太多了 - 这只是堆栈的1/2 GB提交内存。 And then you have all the context switching. 然后你就完成了所有上下文切换。

Good that you want to get rid of the Suspend and Resume calls, but I suggest you have a look at your architecture first - can you move to APM methods ( BeginXXX / EndXXX )? 好你想要摆脱SuspendResume呼叫,但我建议你先看看你的架构 - 你能转向APM方法(BeginXXX / EndXXX)吗?

As a caveat regarding what i'm about to say: It's not clear what your app does; 关于我要说的内容的一个警告:目前还不清楚你的应用程序做了什么; there are many simple methods for using thread pool threads such as TPL, background workers etc. 有许多简单的方法可以使用线程池线程,如TPL,后台工作程序等。

However, if you have threads that you have created (not threadpool) and you want them to communicate then use Monitor.Wait and Monitor.Pulse with a boolean blocking condition. 但是,如果您有已创建的线程(而不是线程池)并且您希望它们进行通信,则使用具有布尔阻塞条件的Monitor.Wait和Monitor.Pulse。

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);
        }
}

Having written a high performance crawler in C#, I can say with some authority that explicitly managing dozens or hundreds of threads is not the best way to go. 在C#中编写了一个高性能的爬虫程序后,我可以说有一些权威明确地管理几十个或几百个线程并不是最好的方法。 It can be done (I did it), but it's painful in the extreme. 它可以完成(我做到了),但它在极端情况下是痛苦的。

That said . 那就是说。 . .

If your application is written the way 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 the threads between downloads is pretty easy. 在下载之间暂停线程非常简单。 I would suggest making two ManualResetEvent instances: one for continue, and one for shutdown. 我建议制作两个ManualResetEvent实例:一个用于继续,一个用于关闭。 These are static so that all crawler threads can access them: 这些是static以便所有爬网程序线程都可以访问它们:

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

Then, each thread uses WaitAny in a loop: 然后,每个线程在循环中使用WaitAny

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 specified ShutdownEvent first. 请注意,当我定义handles数组时,我首先指定了ShutdownEvent The reason is that if multiple items are signaled, WaitAny returns the lowest index that corresponds to a signaled object. 原因是如果发出多个项目信号, WaitAny将返回与发出信号的对象相对应的最低索引。 If the array were populated in the other order, then you wouldn't be able to shut down without pausing first. 如果数组以其他顺序填充,那么您将无法在不先暂停的情况下关闭。

Now, if you want the threads to shut down, call ShutdownEvent.Set . 现在,如果要关闭线程,请调用ShutdownEvent.Set And if you want the threads to pause, call ContinueEvent.Reset When you want the threads to resume, call ContinueEvent.Set . 如果您希望线程暂停,请调用ContinueEvent.Reset如果希望线程恢复,请调用ContinueEvent.Set

Pausing in the middle of a download is a bit more difficult. 在下载过程中暂停会有点困难。 It's possible to do, but the problem is that if you pause for too long the server might timeout. 这是可能的,但问题是如果你停顿太久,服务器可能会超时。 And then you'll have to restart the download from the beginning or, if the server and your code support it, restart the download from the point at which you left off. 然后你必须从头开始重新下载,或者,如果服务器和你的代码支持它,请从你离开的位置重新开始下载。 Either option is rather painful, so I wouldn't suggest trying to pause in the middle of a download. 这两个选项都相当痛苦,所以我不建议尝试在下载过程中暂停。

Not really. 并不是的。 Suspend/Resume are really simple and work fine until they crash your app, eg. 暂停/恢复非常简单,工作正常,直到他们崩溃你的应用程序,例如。 by suspending a thread that has the memory manager or file system locked up :( 通过挂起已锁定内存管理器或文件系统的线程:(

The usual, slightly more complex, approach is to find somewhere in your threads where you can wait. 通常的,稍微复杂一点的方法是在线程中找到可以等待的地方。 In you case, I'm guessing that most of the threads are normally blocked on some IO call anyway and so are 'suspended', so a good place to enforce a 'suspend' is straight after the read in order to catch those threads where the read does return. 在你的情况下,我猜测大多数线程通常在某些IO调用中被阻塞,因此被“暂停”,因此在读取之后执行“挂起”的好地方是为了捕获那些线程读取确实返回。

You can do the actual suspending by checking a global boolean 'isRunning' flag as suggested by @Andrey and, if a suspend is needed, block on a global ManualResetEvent. 您可以通过检查@Andrey建议的全局布尔“isRunning”标志来执行实际挂起,如果需要挂起,则阻止全局ManualResetEvent。 To suspend, clear the event and then the flag. 暂停,清除事件,然后清除标志。 To resume, set the flag and then the event. 要恢复,请设置标志,然后设置事件。

If using globals makes you feel nauseous, you can pass in the ctor a common instance of some class containing the flag, event and 'suspend(), 'resume()' and 'checkForSuspend()' methods. 如果使用全局变量让你觉得恶心,你可以传递一个包含flag,event和'suspend(),'resume()'和'checkForSuspend()'方法的某个类的公共实例。

Rgds, Martin Rgds,马丁

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM