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

C# 如何向应用程序添加暂停/恢复功能?,c#,multithreading,C#,Multithreading,我正在编写一个应用程序,其中大部分工作由后台线程(10-500个线程)完成 我想添加暂停/恢复功能 以前,您可以使用Thread.Suspend和Thread.Resume来执行此操作。但是这些功能现在被认为已经过时了 有没有什么能让我同样轻松地做到这一点 我正在用c#编写软件,你的应用程序做什么 500个线程太多了——仅堆栈就有1/2GB的提交内存。然后你就有了所有的上下文切换 很好,你想摆脱挂起和恢复调用,但我建议你先看看你的架构-你能转移到APM方法(BeginXXX/EndXXX)吗?作

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

我想添加暂停/恢复功能

以前,您可以使用Thread.Suspend和Thread.Resume来执行此操作。但是这些功能现在被认为已经过时了

有没有什么能让我同样轻松地做到这一点

我正在用c#

编写软件,你的应用程序做什么

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


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

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

但是,如果您有已创建的线程(而不是线程池),并且希望它们通信,则使用带有布尔阻塞条件的Monitor.Wait和Monitor.Pulse

e、 g:


不是真的。挂起/恢复非常简单,可以正常工作,直到应用程序崩溃,例如挂起内存管理器或文件系统被锁定的线程:(

通常,稍微复杂一点的方法是在线程中找到可以等待的地方。在您的情况下,我猜大多数线程通常在某些IO调用上被阻塞,因此是“挂起”的,因此在读取之后直接执行“挂起”是一个很好的位置,以便捕获读取返回的线程

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

如果使用globals让您感到恶心,您可以在ctor中传递某个类的公共实例,该类包含标志、事件和'suspend()、'resume()'和'checkForSuspend()'方法

Rgds,
Martin

在用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
    }
}
请注意,当我定义
句柄
数组时,我首先指定了
ShutdownEvent
。原因是,如果有多个项目发出信号,
WaitAny
返回与发出信号的对象相对应的最低索引。如果数组是按其他顺序填充的,那么您将无法在不暂停firs的情况下关闭t

现在,如果要关闭线程,请调用
ShutdownEvent.Set
。如果要暂停线程,请调用
ContinueEvent.Reset
当要恢复线程时,请调用
ContinueEvent.Set


在下载过程中暂停是有点困难的。这是可能的,但是问题是如果你暂停太久,服务器可能会超时。然后你必须从头开始重新下载,或者如果服务器和你的代码支持它,从你离开的点重新开始下载。ER是痛苦的,所以我不会建议在下载过程中暂停。

如果需要任何有用的响应,你需要提供更多的信息。这些线程是做什么的?它们是如何开始的?在什么条件下它们被“暂停”?如果没有更多的信息,就不可能回答这个问题。一个进程中的500个线程是一个巨大的线程数,特别是如果一堆线程大部分时间处于休眠状态。我的建议是重新设计您的体系结构,使其使用的线程数不超过机器中的处理器数。线程非常“重”在C语言中,你应该创造完成任务所需的绝对最小值。你在做什么,你正在创建这么多线程?你可以考虑查看我们发布的异步CTP。明智地使用<代码> DownloadDataTaskAsync <代码>可能会使你的生活变得简单多了。.为什么未来的事情会如此繁重?框架的引力有问题吗?@MartinJames:请原谅框架的粗糙;我没有时间构建它来缩放或绘制它。大多数情况下,这是一个网络应用程序,它从web下载信息。因为它大部分时间都在等待r响应,多线程对我来说似乎是个好主意。如果你知道一个更好的方法来完成它,让我知道
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
    }
}