10

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

为了防止它在每次应用程序想要使用它时被重新初始化(在典型的使用情况下可能是每秒 50 次或几分钟内根本没有),我决定给它一个作业队列,并让它运行在它自己的线程上,检查队列中是否有任何工作。但是,我不完全确定如何制作一个在有或没有工作的情况下无限运行的线程。

以上是我目前的内容,欢迎大家批评指正

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

经过思考:我在想我可能需要优雅地杀死这个线程而不是让它永远运行,所以我想我会添加一个 Job 类型来告诉线程结束。任何关于如何结束这样的线程的想法也很感激。

4

6 回答 6

20

无论如何你都需要,所以你可以和:lockWaitPulse

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

您可能还想查看这个问题,它限制了队列的大小(如果太满则阻塞)。

于 2009-10-14T15:26:23.877 回答
3

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

将其中一项作业项也设为退出命令,以便您可以在该退出线程时向工作线程发出信号

于 2009-10-14T15:26:44.063 回答
1

我已经实现了一个后台任务队列,没有使用任何类型的while循环、脉冲、等待,或者实际上Thread根本没有接触对象。它似乎有效。(我的意思是在过去的 18 个月里,它在生产环境中每天处理数千个任务,没有任何意外行为。)它是一个具有两个重要属性的类, aQueue<Task>和 a 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

于 2009-10-15T07:17:01.060 回答
1

在大多数情况下,我所做的与您的设置非常相似——但不是使用相同的语言。我具有使用数据结构(在 Python 中)的优势,该结构将阻塞线程,直到将项目放入队列中,从而无需 sleep 调用。

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

你能通过的工作可以简单到“空”;如果代码收到一个空值,它就知道是时候跳出这段时间回家了。

于 2009-10-14T15:28:24.870 回答
1

抓住并行框架。它有一个可以用作作业队列的BlockingCollection<T> 。你将如何使用它是:

  1. 创建将保存您的任务/作业的 BlockingCollection<T>。
  2. 创建一些具有永无止境的循环的线程 (while(true){ // 让工作脱离队列)
  3. 设置线程
  4. 可用时将作业添加到集合中

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

它还具有依靠 MS 编写多线程访问同一资源的特别讨厌的代码的优势。每当你可以让别人写你应该去写。当然,假设他们比您拥有更多的技术/测试资源和综合经验。

于 2009-10-14T15:28:50.067 回答
1

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

于 2009-10-14T15:30:47.800 回答