Планировщик заданий, использующий многопоточность с приоритетом - PullRequest
3 голосов
/ 07 февраля 2011

Я собираюсь запустить проект C # .NET 4.0, который создает планировщик заданий.

  1. Задание не имеет даты выполнения и потенциально может выполняться долго, вплоть до дней.
  2. Работа имеет 3 приоритета: бездействующий, обычный, критический;от низшего к высшему.
  3. Постоянно создаются новые задания.
  4. Более новое задание с более высоким приоритетом должно иметь приоритет над заданием с более низким приоритетом, даже если старое задание было создано в течение длительного времени.
  5. Каждое задание будет обрабатываться одним продолжительным потоком.
  6. Задание является входящим повторно.Состояние задания сохраняется в базе данных, поэтому можно приостановить задание или прекратить поток задания в любое время.

Я планирую использовать семафор и задать количество одновременных записей.на количество ядер системы.Для каждого задания в очереди будет создан новый поток, и весь поток будет заблокирован семафором в начале.

Моя проблема в том, чтобы поток с высоким приоритетом сначала входил в семафор, когда семафор вызывает метод release ().Выполнимо?

Моя вторая проблема состоит в том, чтобы иметь поток, находящийся внутри семафора, для выхода при появлении потока задания с более высоким приоритетом, и заставить выходящий поток задания вернуться в очередь потока, чтобы дождаться семафора.Выполнимо?

Для этих двух проблем подходит ли семафор?Если нет, что вы предлагаете?

Ответы [ 3 ]

4 голосов
/ 07 февраля 2011

Ну, я бы больше склонялся к чему-то вроде следующего ...

Во-первых, запустите все потоки, которые вы хотите:

for(int i=0; i < Environment.ProcessorCount; i++)
{
    Thread t = new Thread(RunWork);
    // setup thread
    t.Start();
    threads.Add(t);
}

Вам потребуется интерфейс для описания приоритетазадачи

interface ITask {
    PrioirtyType Prioirty { get; }
    bool Complete { get; }
    void PerformOneUnitOfWork();
}

Затем создайте объект управления очередью.Это, очевидно, усложняется, поскольку может потребоваться синхронизация с вашей базой данных и т. Д ...

class MyQueue<TJob> where TJob : ITask 
{
    Queue<TJob> high, med, low;
    bool GetNextJob(ref TJob work)
    {
        if(work.Priority == PriorityType.High && !work.Complete)
            return true;
        lock(this)
        {
            if(high.Count > 0)
            {
                Enqueue(work);//requeue to pick back up later
                work = high.Dequeue();
                return true;
            }
            if(work.Priority == PriorityType.Med && !work.Complete)
                return true;
            if(med.Count > 0)
            {
                Enqueue(work);//requeue to pick back up later
                work = med.Dequeue();
                return true;
            }
            if(!work.Complete)
                return true;
            if(low.Count > 0)
            {
                work = low.Dequeue();
                return true;
            }
            work = null;
            return false;
        }

    void Enqueue(TJob work)
    {
        if(work.Complete) return;
        lock(this)
        {
            else if(work.Priority == PriorityType.High) high.Enqueue(work);
            else if(work.Priority == PriorityType.Med) med.Enqueue(work);
            else low.Enqueue(work);
        }
    }
}

И, наконец, создайте свой рабочий поток примерно так:

public void RunWork()
{
    ITask job;
    while(!_shutdown.WaitOne(0))
    {
        if(queue.GetNextJob(ref job))
            job.PerformOneUnitOfWork();
        else
            WaitHandle.WaitAny(new WaitHandle[] { _shutdown, queue.WorkReadyHandle });
    }
}
3 голосов
/ 07 февраля 2011

Для этих двух проблем подходит ли семафор?Если не то, что вы предлагаете?

Это действительно зависит.Часто лучше иметь более одного задания на поток, так как многие (особенно долго работающие) рабочие элементы будут тратить свое время на ожидание чего-то другого, кроме процессора.Например, если вы выполняете работу, когда вы получаете услуги WCF или другие связанные с этим проблемы, вы можете проводить много времени в заблокированном и бездействующем режиме.

В этом случае может быть лучше простопусть ваш график работы по мере необходимости.Использование ThreadPool может быть более приятным в сценарии такого типа.

Если, однако, все задания имеют высокую загрузку ЦП, тогда ваш подход может быть желательным.Очередь приоритетов может использоваться для отслеживания приоритета планирования и определения того, какое задание запускать.

При этом, я бы, вероятно, не использовал для этого семафор.Хотя он будет работать, один счетчик (управляемый через Interlocked.Increment / Decrement) и ManualResetEvent будет работать так же хорошо и будет намного легче.

0 голосов
/ 21 мая 2012

Существует отличный фреймворк, который был перенесен с Java на C #. Проверьте это

http://quartznet.sourceforge.net/

...