我可以在任何时候同时运行最多5个线程,这使得使用5个单独的硬件来加速一些复杂计算的计算并返回结果。每种硬件的API(只包含一个方法)都不是线程安全的,在任何时候都只能在单个线程上运行。计算完成后,根据可用性,可以重用相同的线程在相同或不同的硬件上启动另一个计算。每个计算都是独立的,不依赖于其他计算的结果。因此,最多可以有5个线程以任意顺序完成它的执行。
什么是最有效的c#(使用。net Framework 2.0)编码解决方案来跟踪哪些硬件是空闲的/可用的,并分配一个线程给适当的硬件API来执行计算?请注意,除了5个并发运行线程的限制之外,我无法控制线程何时或如何被触发。如果我错了,请纠正我,但一个无锁的解决方案是首选,因为我相信它将导致提高效率和更具可扩展性的解决方案。
。NET提供了一个可以使用的线程池。System.Threading.ThreadPool.QueueUserWorkItem()
告诉池中的线程为您做一些工作。
如果我设计这个,我不会把重点放在将线程映射到你的HW资源上。相反,我将为每个硬件资源公开一个可锁定的对象——这可以简单地是一个包含5个对象的数组或队列。然后对于每一个计算位,调用QueueUserWorkItem()
。在传递给QUWI的方法中,找到下一个可用的可锁定对象并将其锁定(也就是将其从队列中取出)。使用HW资源,然后重新为对象排队,退出QUWI方法。
不管你打了多少次QUWI;最多可以持有5个锁,每个锁保护访问您的特殊硬件设备的一个实例。
Monitor.Enter()
的文档页面显示了如何创建一个安全(阻塞)队列,可以由多个工人访问。在。net 4.0中,你会使用内置的BlockingCollection——这是一样的。
这基本上就是你想要的。除了不要调用Thread.Create()
。使用线程池
引用:使用线程的优势。Start vs 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();
}
只有在计算时间非常短的情况下,无锁的解决方案才有益。
我将为每个硬件线程创建一个facade,其中作业被排队,每次作业完成时调用一个回调。
类似:
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();
}
}
}
听起来您需要一个包含5个线程的线程池,其中每个线程在完成后放弃HW并将其添加回某个队列。这样行吗?如果是这样,. net使线程池非常容易。
听起来很像睡觉的理发师问题。我认为标准的解决方案是使用信号量