如何在应用程序中添加暂停/恢复功能

本文关键字:暂停 恢复 功能 添加 应用程序 | 更新日期: 2023-09-27 18:10:48

我正在编写一个应用程序,其中大部分工作是由后台线程完成的(10 - 500线程)。

我想添加暂停/继续功能。

以前,你可以用Thread来做。Suspend和Thread.Resume。但是这些功能现在被认为已经过时了。

有没有什么东西可以让我同样轻松地做同样的事情?

我正在用c#编写软件

如何在应用程序中添加暂停/恢复功能

我用c#写了一个高性能的爬虫,我可以很权威地说,显式地管理几十个或几百个线程并不是最好的方法。这是可以做到的(我做到了),但它是极端痛苦的。

话虽如此……

如果你的应用程序是按照我的想法编写的,那么每个线程都是这样做的:

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

在下载之间暂停线程非常简单。我建议创建两个ManualResetEvent实例:一个用于继续,一个用于关闭。这些是static,以便所有爬虫线程都可以访问它们:

static ManualResetEvent ShutdownEvent = new ManualResetEvent(false);
static ManualResetEvent ContinueEvent = new ManualResetEvent(true);
然后,每个线程在循环中使用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
    }
}

注意,在定义handles数组时,我首先指定了ShutdownEvent。原因是,如果多个项目被标记,WaitAny返回与被标记对象对应的最低索引。如果数组是按其他顺序填充的,那么不先暂停就无法关闭。

现在,如果您想关闭线程,调用ShutdownEvent.Set。如果你想线程暂停,调用ContinueEvent.Reset;如果你想线程恢复,调用ContinueEvent.Set

在下载过程中暂停有点困难。这是可能的,但问题是,如果您暂停太长时间,服务器可能会超时。然后,您必须从头开始重新开始下载,或者,如果服务器和您的代码支持,则从您停止的位置重新开始下载。这两种选择都很痛苦,所以我不建议在下载过程中暂停。

你的应用是做什么的?

500个线程太多了——仅仅是堆栈就占用了1/2 GB的已提交内存。然后你有所有的上下文切换。

很好,你想摆脱SuspendResume调用,但我建议你先看看你的架构-你能转移到APM方法(BeginXXX/EndXXX)吗?

我要说的是:你的应用程序做什么并不清楚;有许多简单的方法可以使用线程池线程,如TPL、后台工作线程等。

但是,如果您有创建的线程(不是线程池),并且希望它们通信,则使用Monitor。等待和监控。带布尔阻塞条件的脉冲。

例句:

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

不完全是。暂停/恢复真的很简单,工作很好,直到他们崩溃你的应用程序,例如。通过挂起内存管理器或文件系统被锁定的线程:(

通常,稍微复杂一点的方法是在线程中找到一个可以等待的地方。在你的情况下,我猜大多数线程通常在一些IO调用上被阻塞,因此被'挂起',所以强制'挂起'的好地方是在读取之后,为了捕捉那些读取返回的线程。

你可以通过检查全局布尔'isRunning'标志来执行实际的挂起,@Andrey建议,如果需要挂起,阻塞全局ManualResetEvent。要挂起,先清除事件,然后清除标志。要继续,先设置标志,再设置事件。

如果使用全局变量让你感到恶心,你可以在actor中传递一些类的通用实例,其中包含flag, event和'suspend(), 'resume()'和'checkForSuspend()'方法。

祝好,马丁