2011-10-10 110 views
6

我正在编写一个应用程序,其中大部分工作由后台线程完成(10 - 500个线程)。如何将暂停/恢复功能添加到应用程序?

我想添加暂停/恢复功能。

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

有什么可以让我在平等的情况下做同样的事吗?

我正在用c编写软件#

+0

如果您需要任何有用的回应,您需要提供更多信息。这些线程在做什么?他们是如何开始的?他们在什么条件下被“暂停?”没有更多的信息,回答这个问题是不可能的。 –

+2

一个进程中的500个线程是一个*巨大数量的线程,特别是如果其中一堆线程大部分时间处于睡眠状态。我的建议是重新设计你的架构,以便不使用比机器中的处理器更多的线程。线程在C#中非常“重”;你应该创造完成工作所需的绝对最低限度。你在做什么,你正在创造这么多的线程? –

+0

这是一个网络爬行应用程序。每个线程花费大部分时间等待来自某个Web服务器的响应,所有线程都在相同的地方启动,并且应该在用户按下“暂停”按钮时暂停。该应用程序通常需要700m-2G的内存,除非有更好的方法来做到这一点,否则我可以接受。即使在500个线程下,应用程序通常也只有不到30%的单个CPU运行 –

回答

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

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

2

你的应用做什么?

500个线程太多 - 这是1/2 GB的承诺内存仅用于堆栈。然后你有所有的上下文切换。

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

+0

这是一个网络应用程序,它从网上下载信息。因为它大部分时间都在等待响应,多线程对我来说似乎是个好主意。如果你知道更好的方法来完成它,请告诉我 –

2

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

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

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

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

通常的,稍微复杂的方法是在你的线程中找到你可以等待的地方,在你的情况下,我猜测最的线程通常在某些IO调用上被阻塞,因此被“挂起”,因此在读取后执行“暂停”的好地方是为了捕获读取返回的线程。可以通过检查@Andrey建议的全局布尔'isRunning'标志来实现实际挂起,如果需要挂起,则阻止全局ManualResetEvent挂起,清除事件,然后清除标志。然后事件

如果使用全局变量会让你感到恶心,那么你可以传入一个包含标志,事件和'suspend(),'resume()'和'checkForSuspend()'方法的类的公共实例。

RGDS, 马丁