Правильный способ иметь бесконечную рабочую нить?

У меня есть объект, который требует много инициализации (1-2 секунды на мускулистый машина). Хотя после его инициализации требуется всего около 20 миллисекунд, чтобы выполнить типичную "работу"

чтобы предотвратить его повторную инициализацию каждый раз, когда приложение хочет его использовать (что может быть 50 раз в секунду или вообще не в течение нескольких минут при обычном использовании), я решил дать ему задание que и запустить его в своем собственном потоке, проверяя, есть ли для него работа в que. Однако Я не совсем уверен, как сделать нитью indefinetly С или без работы.

вот что у меня до сих пор, любая критика приветствуется

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

After thought: Я думал, что мне может понадобиться изящно убить этот поток, чтобы позволить ему работать вечно, поэтому я думаю, что добавлю тип задания, который говорит потоку закончить. Любые мысли о том, как закончить такую нить, также оценили.

6 ответов


вы нужно to lock в любом случае, так что вы можете Wait и Pulse:

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

вы также можете посмотреть этот вопрос, который ограничивает размер очереди (блокировка, если она слишком полная).


вам нужен примитив синхронизации, как WaitHandle (посмотрите на статические методы) . Таким образом, вы можете "сигнализировать" рабочему потоку, что есть работа. Он проверяет очередь и продолжает работать до тех пор, пока очередь не опустеет, и тогда он ждет, что мьютекс снова подаст сигнал.

сделайте один из элементов задания командой quit, чтобы вы могли сигнализировать рабочему потоку, когда пришло время выйти из потока


в большинстве случаев я сделал это очень похоже на то, как вы настроили-но не на том же языке. У меня было преимущество работы со структурой данных (в Python), которая будет блокировать поток, пока элемент не будет помещен в очередь, отрицая необходимость вызова сна.

Если .NET предоставляет такой класс, я бы посмотрел на его использование. Блокировка потока намного лучше, чем поток, вращающийся при вызовах сна.

задание, которое вы можете передать, может быть таким же простым, как " null"; если код получает null, он знает, что пришло время вырваться из while и вернуться домой.


Если вам действительно не нужно иметь выход потока (и просто хотите, чтобы он не поддерживал ваше приложение), вы можете установить нить.IsBackground к true, и он закончится, когда все не фоновые потоки закончатся. Уилл и Марк имеют хорошие решения для обработки очереди.


возьмите Параллельные Структуры. У него есть BlockingCollection что можно использовать в качестве очереди заданий. Как вы его используете:

  1. создайте BlockingCollection, который будет содержать ваши задачи / задания.
  2. Создайте несколько потоков, которые имеют бесконечный цикл (while (true) {//get job off the queue)
  3. установите потоки
  4. добавить задания в коллекцию, когда они становятся доступными

в потоки будут заблокированы до тех пор, пока элемент не появится в коллекции. Кто бы ни повернул, он получит его (зависит от процессора). Я использую это сейчас, и это отлично работает.

Он также имеет преимущество полагаться на MS, чтобы написать этот особенно неприятный бит кода, где несколько потоков обращаются к одному и тому же ресурсу. И всякий раз, когда вы можете заставить кого-то другого написать, что вы должны пойти на это. Предполагая, конечно, что у них больше технических/тестовых ресурсов и Объединенного опыта, чем у вас.


я реализовал очередь фоновых задач без использования каких-либо while петля, или пульсация, или ожидание, или, действительно, прикосновение Thread объекты на всех. И это, кажется, работает. (Под этим я имею в виду, что в течение последних 18 месяцев в производственных средах обрабатывались тысячи задач в день без какого-либо неожиданного поведения.) Это класс с двумя значимыми свойствами,Queue<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 будет делать?