我正在编写一个应用程序,其中大部分工作都是由后台线程(10 - 500个线程)完成的。
我想添加暂停/恢复功能。
之前,你可以使用Thread.Suspend和Thread.Resume来做到这一点。但是这些功能现在已经过时了。
还有什么可以让我同样轻松地做同样的事情吗?
我正在用c#编写软件
答案 0 :(得分:2)
你的应用做了什么?
500个线程太多了 - 这只是堆栈的1/2 GB提交内存。然后你就完成了所有的上下文切换。
很好,你想摆脱Suspend
和Resume
电话,但我建议你先看看你的架构 - 你可以转向APM方法(BeginXXX / EndXXX)吗? / p>
答案 1 :(得分:2)
关于我要说的内容的一个警告:目前还不清楚你的应用是做什么的;有许多简单的方法可以使用线程池线程,如TPL,后台工作程序等。
但是,如果你有你创建的线程(而不是线程池),并且你希望它们进行通信,那么使用具有布尔阻塞条件的Monitor.Wait和Monitor.Pulse。
e.g:
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 :(得分:2)
在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
。
在下载过程中暂停会有点困难。这是可能的,但问题是如果你停顿太久,服务器可能会超时。然后你必须从头开始重新下载,或者,如果服务器和你的代码支持它,请从你离开的位置重新开始下载。这两个选项都相当痛苦,因此我不建议尝试在下载过程中暂停。
答案 3 :(得分:1)
不是真的。暂停/恢复非常简单,工作正常,直到他们崩溃你的应用程序,例如。通过挂起已锁定内存管理器或文件系统的线程:(
通常的,稍微复杂一点的方法是找到线程中可以等待的地方。在你的情况下,我猜测大多数线程通常在某些IO调用中被阻塞,因此被“暂停”,因此在读取之后执行“挂起”的好地方是为了捕获那些线程读取确实会返回。
您可以通过检查@Andrey建议的全局布尔值'isRunning'标志来执行实际挂起,如果需要挂起,则阻止全局ManualResetEvent。暂停,清除事件,然后清除标志。要恢复,请设置标志,然后设置事件。
如果使用全局变量让你感到恶心,你可以传递一个包含flag,event和'suspend(),'resume()'和'checkForSuspend()'方法的某个类的公共实例。
RGDS, 马丁