根据以下场景,分配线程的最有效方法是什么?

本文关键字:有效 方法 是什么 线程 分配 | 更新日期: 2023-09-27 18:10:29

我可以在任何时候同时运行最多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使线程池非常容易。

听起来很像睡觉的理发师问题。我认为标准的解决方案是使用信号量