Каков наиболее эффективный метод назначения потоков на основе следующего сценария? - PullRequest
2 голосов
/ 04 октября 2011

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

Какое наиболее эффективное решение для кодирования на C # (с использованием .Net Framework 2.0) позволяет отслеживать, какое оборудование свободно / доступно, и назначать поток для соответствующего аппаратного API для выполнения вычислений? Обратите внимание, что кроме ограничения 5 одновременно работающих потоков, я не имею никакого контроля над тем, когда и как запускаются потоки.

Пожалуйста, исправьте меня, если я ошибаюсь, но решение без блокировки предпочтительнее, так как я считаю, что это приведет к повышению эффективности и более масштабируемому решению.

Также обратите внимание, что это не домашняя работа, хотя может звучать так ...

Ответы [ 4 ]

2 голосов
/ 04 октября 2011

.NET предоставляет пул потоков, который вы можете использовать. System.Threading.ThreadPool.QueueUserWorkItem() говорит потоку в пуле сделать для вас некоторую работу.

Если бы я проектировал это, я бы не стал фокусироваться на сопоставлении потоков с вашими ресурсами HW. Вместо этого я бы выставил блокируемый объект для каждого ресурса HW - это может быть просто массив или очередь из 5 объектов. Затем для каждого имеющегося у вас вычисления вызовите QueueUserWorkItem(). Внутри метода, который вы передаете в QUWI, найдите следующий доступный блокируемый объект и заблокируйте его (иначе, удалите из очереди). Используйте ресурс HW, затем повторно поставьте объект в очередь, выйдите из метода QUWI.

Не имеет значения, сколько раз вы звоните в QUWI; может удерживаться не более 5 блокировок, каждая из которых защищает доступ к одному экземпляру вашего специального аппаратного устройства.

Страница документа для Monitor.Enter() показывает, как создать безопасную (блокирующую) очередь, к которой могут обращаться несколько работников. В .NET 4.0 вы бы использовали встроенную коллекцию BlockingCollection - это то же самое.

Это в основном то, что вы хотите. Кроме не звоните Thread.Create(). Используйте пул потоков.

cite: Преимущество использования Thread.Start по сравнению с QueueUserWorkItem


// assume the SafeQueue class from the cited doc page. 
SafeQueue<SpecialHardware> q = new SafeQueue<SpecialHardware>()

// set up the queue with objects protecting the 5 magic stones
private void Setup() 
{
    for (int i=0; i< 5; i++) 
    {
       q.Enqueue(GetInstanceOfSpecialHardware(i));
    }
}


// something like this gets called many times, by QueueUserWorkItem()
public void DoWork(WorkDescription d)
{
    d.DoPrepWork();

    // gain access to one of the special hardware devices
    SpecialHardware shw = q.Dequeue();
    try 
    {
        shw.DoTheMagicThing();
    }
    finally 
    {
        // ensure no matter what happens the HW device is released
        q.Enqueue(shw);
        // at this point another worker can use it.
    }

    d.DoFollowupWork(); 
}
1 голос
/ 04 октября 2011

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

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

Что-то вроде:

public class Job
{
    public string JobInfo {get;set;}
    public Action<Job> Callback {get;set;}
}

public class MyHardwareService
{
    Queue<Job> _jobs = new Queue<Job>();
    Thread _hardwareThread;
    ManualResetEvent _event = new ManualResetEvent(false);

    public MyHardwareService()
    {
        _hardwareThread = new Thread(WorkerFunc);
    }

    public void Enqueue(Job job)
    {
      lock (_jobs)
        _jobs.Enqueue(job);

       _event.Set();
    }

    public void WorkerFunc()
    {
        while(true)
        {
             _event.Wait(Timeout.Infinite);
             Job currentJob;
             lock (_queue)
             {
                currentJob = jobs.Dequeue();
             }

             //invoke hardware here.

             //trigger callback in a Thread Pool thread to be able
             // to continue with the next job ASAP
             ThreadPool.QueueUserWorkItem(() => job.Callback(job));

            if (_queue.Count == 0)
              _event.Reset();

        }
    }
}
0 голосов
/ 04 октября 2011

Похоже на Проблема спящего парикмахера . Я считаю, что стандартное решение для этого заключается в использовании семафоров

0 голосов
/ 04 октября 2011

Похоже, вам нужен пул потоков с 5 потоками, каждый из которых освобождает HW после завершения и добавляет его обратно в некоторую очередь.Будет ли это работать?Если это так, .Net делает пулы потоков очень легкими.

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