2009-10-14 63 views
10

我有一个需要大量初始化的对象(在强壮的机器上1-2秒)。虽然一旦初始化,它只需要大约20毫秒来做一个典型的“工作”正确的方式有一个无休止的工作线程?

为了防止它每次应用程序想要使用它重新初始化(可能是每秒50次在典型的使用中只需几分钟),我决定给它一个job que,并让它在自己的线程上运行,检查是否有任何工作。然而,我不完全确定如何使一个线程无论运行还是不运行。

这是我到目前为止,任何批评是值得欢迎的

private void DoWork() 
    { 
     while (true) 
     { 
      if (JobQue.Count > 0) 
      { 
       // do work on JobQue.Dequeue() 
      } 
      else 
      { 
       System.Threading.Thread.Sleep(50); 
      } 
     } 
    } 

左思右想:我想我可能需要杀死这个线程优雅的欧洲工商管理学院让它永远运行下去的,所以我想我会加告诉线程结束的作业类型。关于如何结束这样的线程的任何想法也赞赏。

回答

19

需要lock,所以你可以WaitPulse

while(true) { 
    SomeType item; 
    lock(queue) { 
     while(queue.Count == 0) { 
      Monitor.Wait(queue); // releases lock, waits for a Pulse, 
           // and re-acquires the lock 
     } 
     item = queue.Dequeue(); // we have the lock, and there's data 
    } 
    // process item **outside** of the lock 
} 

与补充,如:

lock(queue) { 
    queue.Enqueue(item); 
    // if the queue was empty, the worker may be waiting - wake it up 
    if(queue.Count == 1) { Monitor.PulseAll(queue); } 
} 

你也可能想看看this question,这限制了大小队列(如果队列太满则阻塞)。

+0

经典队列,锁定和脉动。 – 2009-10-14 15:28:30

+0

+1非常好... – 2009-10-14 15:30:24

+0

为什么写一些已经写好的东西?宝贝,宝贝。 – Will 2009-10-14 15:30:45

2

您需要一个同步原语,如WaitHandle(查看静态方法)。通过这种方式,您可以“通知”工作线程有工作。它检查队列并继续工作,直到队列为空,此时它会等待互斥信号再次发出信号。

使工作项目之一是quit命令也一样,这样就可以发出信号的工作线程时,它的时间才能退出线程

1

在大多数情况下,我已经做到了这一点颇为相似,你怎么”已经建立 - 但不是在相同的语言。我有一个使用数据结构(使用Python)的优势,该数据结构将阻塞线程,直到某个项目放入队列中,从而无需进行睡眠调用。

如果.NET提供这样的类,我会考虑使用它。线程阻塞比睡眠呼叫旋转的线程要好得多。

你可以传递的工作可以像“null”一样简单;如果代码收到null,它就知道是时候离开并回家了。

1

抓住Parallel Framework。它有一个BlockingCollection<T>,您可以将其用作作业队列。你将如何使用它:

  1. 创建BlockingCollection < T>将保存您的任务/作业。
  2. 创建一些线程其中有一个永无休止的循环(而(真){//获取作业从队列中)
  3. 设置线程去
  4. 添加作业,当他们来到可用的集合

线程将被阻塞,直到项目出现在集合中。不管轮到谁都会得到它(取决于CPU)。我现在正在使用它,它工作得很好。

它还具有依靠MS编写多线程访问相同资源的特别讨厌的代码的优点。只要你能让其他人写下你应该去做的。当然,假设他们拥有比您更多的技术/测试资源和相结合的经验。

+0

您的意思是标注为“此CTP仅用于测试目的”的CTP?不知道这是一个伟大的建议...在4.0,很好 - 但这是测试! – 2009-10-14 15:45:22

1

如果您确实不需要让线程退出(并且只是希望它不会让应用程序继续运行),那么您可以将Thread.IsBackground设置为true,并在所有非后台线程结束时结束。 Will和Marc都有很好的解决方案来处理队列。

1

我已经实现了一个后台任务队列,但没有使用任何种类的while循环,脉冲或等待,或者实际上完全触及Thread对象。它似乎工作。 (我的意思是说,它一直在生产环境中,在过去的18个月中每天处理数千个任务而没有任何意外的行为。)这是一个包含两个重要属性的类,Queue<Task>BackgroundWorker。有三种重要的方法,这里缩写:

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    if (TaskQueue.Count > 0) 
    { 
     TaskQueue[0].Execute(); 
    } 
} 

private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    Task t = TaskQueue[0]; 

    lock (TaskQueue) 
    { 
     TaskQueue.Remove(t); 
    } 
    if (TaskQueue.Count > 0 && !BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

public void Enqueue(Task t) 
{ 
    lock (TaskQueue) 
    { 
     TaskQueue.Add(t); 
    } 
    if (!BackgroundWorker.IsBusy) 
    { 
     BackgroundWorker.RunWorkerAsync(); 
    } 
} 

这并不是说没有等待和脉动。但是这一切都发生在BackgroundWorker之内。只要任务在队列中被放下,一直运行,直到队列为空,然后再返回到休眠状态,它就会醒来。

我远离线程专家。如果使用BackgroundWorker会出现这样的问题,是否有理由惹恼System.Threading

+0

一般来说,我同意BackgroundWorker类通常是管理后台任务的简单选择。在这种情况下,我不得不挑战你的一些主张。您正在使用线程对象:'lock'关键字是'System.Threading.Monitor.Enter()'和'System.Threading.Monitor.Exit()'的语法糖。而不是一个while循环,你的RunWorkerCompleted事件处理程序调用'BackgroundWorker.RunWorkerAsync()'。在这种情况下,我认为等待/脉冲while循环的逻辑可能更容易遵循。 – 2010-08-19 22:38:09

+0

够公平的。这可能对我来说更简单,因为我没有经历过等待/脉搏。 – 2010-08-19 23:41:45