有一个无尽的工人线程的正确方法?

时间:2009-10-14 15:21:57

标签: c# .net multithreading worker-thread

我有一个需要大量初始化的对象(在强壮的机器上1-2秒)。虽然一旦初始化,它只需要大约20毫秒来完成一个典型的“工作”

为了防止每次应用想要使用它时重新初始化它(在典型的使用情况下可能是每秒50次或完全没有),我决定给它一个工作,并且让它在自己的线程上运行,检查在que中是否有任何工作。但是,我并不完全确定如何制作一个无论是否有工作而无条件运行的线程。

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

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

经过深思熟虑:我以为我可能需要优先杀死这个线程让它永远运行,所以我想我会添加一个告诉线程结束的Job类型。关于如何结束这样的线程的任何想法也表示赞赏。

6 个答案:

答案 0 :(得分: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,这会限制队列的大小(如果它太满则会阻塞)。

答案 1 :(得分:2)

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

使其中一个作业项也是一个退出命令,这样你可以在退出线程时发出工作线程的信号

答案 2 :(得分:1)

在大多数情况下,我这样做与您的设置非常相似 - 但不是使用相同的语言。我有一个使用数据结构(在Python中)的优势,它将阻塞线程直到项目被放入队列,从而无需进行睡眠调用。

如果.NET提供类似的类,我会考虑使用它。线程阻塞比在休眠调用中旋转的线程要好得多。

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

答案 3 :(得分:1)

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

  1. 创建BlockingCollection&lt; T&gt;这将保留你的任务/工作。
  2. 创建一些具有永无止境循环的线程(while(true){//从队列中取出作业)
  3. 设置线程
  4. 当作业可用时添加作业
  5. 线程将被阻止,直到项目出现在集合中。无论谁转过它都会得到它(取决于CPU)。我现在正在使用它,效果很好。

    它还有一个优势,就是依靠MS来编写特别讨厌的代码,其中多个线程访问相同的资源。无论什么时候你都可以让其他人写下你应该去的。当然,假设他们拥有比您更多的技术/测试资源和综合经验。

答案 4 :(得分:1)

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

答案 5 :(得分: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内。这只是在队列中删除任务时才会唤醒,运行直到队列为空,然后再回到睡眠状态。

我远不是线程专家。如果使用System.Threading会有问题,是否有理由对BackgroundWorker这样的问题进行处理?