Каков наиболее эффективный для ЦП способ заставить рабочие потоки ждать выполнения задач? - PullRequest
5 голосов
/ 14 января 2010

В моем текущем приложении C # / NET 3.5 у меня есть очередь задач (поточно-ориентированная), и у меня есть 5 рабочих потоков, которые должны постоянно искать задачи в очереди. Если задача доступна, любой работник откажется от нее и предпримет необходимые действия.

Мой класс рабочих потоков выглядит следующим образом:

public class WorkerThread
{
    //ConcurrentQueue is my implementation of thread safe queue
    //Essentially just a wrapper around Queue<T> with synchronization locks
    readonly ConcurrentQueue<CheckPrimeTask> mQ; 
    readonly Thread mWorker;
    bool mStop;

    public WorkerThread (ConcurrentQueue<CheckPrimeTask> aQ) {
        mQ = aQ;
        mWorker = new Thread (Work) {IsBackground = true};
        mStop = false;
    }

    private void Work () {
        while (!mStop) {
            if (mQ.Count == 0) {
                Thread.Sleep (0);
                continue;
            }

            var task = mQ.Dequeue ();
            //Someone else might have been lucky in stealing
            //the task by the time we dequeued it!!
            if (task == null) 
                continue;

            task.IsPrime = IsPrime (task.Number);
            task.ExecutedBy = Thread.CurrentThread.ManagedThreadId;
            //Ask the threadpool to execute the task callback to 
            //notify completion
            ThreadPool.QueueUserWorkItem (task.CallBack, task);
        }
    }

    private bool IsPrime (int number) {
        int limit = Convert.ToInt32 (Math.Sqrt (number));
        for (int i = 2; i <= limit; i++) {
            if (number % i == 0)
                return false;
        }

        return true;
    }

    public void Start () {
        mStop = false;
        mWorker.Start ();
    }

    public void Stop () {
        mStop = true;
    }
}

Проблема в том, что когда очередь пуста, она потребляет слишком много ресурсов ЦП (почти 98%). Я попытался AutoResetEvent, чтобы уведомить работников, что очередь была изменена. Таким образом, они эффективно ждут, чтобы установить этот сигнал. Он снизил нагрузку на процессор почти до 0%, но я не совсем уверен, является ли это лучшим методом. Можете ли вы предложить лучший способ, чтобы потоки простаивали без ущерба для загрузки процессора?

Ответы [ 4 ]

7 голосов
/ 14 января 2010

Проверьте эту реализацию BlockingQueue . Если очередь пуста, она использует Monitor.Wait (), чтобы перевести поток в спящий режим. Когда элемент добавляется, он использует Monitor.Pulse () для пробуждения потока, который спит в пустой очереди.

Другой метод - использовать семафор . Каждый раз, когда вы добавляете элемент в очередь, вызывайте Release (). Если вы хотите получить элемент из очереди, вызовите WaitOne ().

2 голосов
/ 14 января 2010

В настоящее время у вас есть Thread.Sleep(0) в вашем методе работы, для которого нет элементов очереди. Измените его на что угодно больше 0, и загрузка вашего процессора снизится. Попробуйте 10 начать с ...

0 голосов
/ 05 февраля 2010

Если ваша очередь потокобезопасна, вам не нужно этого делать ...

    //Someone else might have been lucky in stealing 
    //the task by the time we dequeued it!! 
    if (task == null)  
        continue; 
0 голосов
/ 14 января 2010

У вас есть пара вариантов, о которых я могу подумать.

Один из способов - разместить небольшой нить в течение цикла. Это в основном снизит загрузку вашего процессора до 0 и является довольно стандартным способом сделать это.

Другой способ - использовать сброс (автоматический или ручной), как предложил Митч Уит в комментариях.

Вы могли бы также разработать какую-то IdleTask, в которой поток спит в течение определенного времени, и если ваша очередь пуста, просто обработайте IdleTask (который будет спить в потоке).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...