开发者

What is the most efficient method for assigning threads based on the following scenario?

开发者 https://www.devze.com 2023-04-11 05:35 出处:网络
I can have a maximum of 5 threads running simultaneous at any one time which makes use of 5 separate hardware to speedup the computation of some complex calculations and return the result. The API (co

I can have a maximum of 5 threads running simultaneous at any one time which makes use of 5 separate hardware to speedup the computation of some complex calculations and return the result. The API (contains only one method) for each of this hardware is not thread safe and can only run on a single thread at any point in time. Once the computation is completed, the same thread can be re-used to start another computation on either the same or a different hardware depending on availability. Each computation 开发者_如何学Cis stand alone and does not depend on the results of the other computation. Hence, up to 5 threads may complete its execution in any order.

What is the most efficient C# (using .Net Framework 2.0) coding solution for keeping track of which hardware is free/available and assigning a thread to the appropriate hardware API for performing the computation? Note that other than the limitation of 5 concurrently running threads, I do not have any control over when or how the threads are fired.

Please correct me if I am wrong but a lock free solution is preferred as I believe it will result in increased efficiency and a more scalable solution.

Also note that this is not homework although it may sound like it...


.NET provides a thread pool that you can use. System.Threading.ThreadPool.QueueUserWorkItem() tells a thread in the pool to do some work for you.

Were I designing this, I'd not focus on mapping threads to your HW resources. Instead I'd expose a lockable object for each HW resource - this can simply be an array or queue of 5 Objects. Then for each bit of computation you have, call QueueUserWorkItem(). Inside the method you pass to QUWI, find the next available lockable object and lock it (aka, dequeue it). Use the HW resource, then re-enqueue the object, exit the QUWI method.

It won't matter how many times you call QUWI; there can be at most 5 locks held, each lock guards access to one instance of your special hardware device.

The doc page for Monitor.Enter() shows how to create a safe (blocking) Queue that can be accessed by multiple workers. In .NET 4.0, you would use the builtin BlockingCollection - it's the same thing.

That's basically what you want. Except don't call Thread.Create(). Use the thread pool.

cite: Advantage of using Thread.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(); 
}


A lock free solution is only beneficial if the computation time is very small.

I would create a facade for each hardware thread where jobs are enqueued and a callback is invoked each time a job finishes.

Something like:

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();

        }
    }
}


Sounds like you need a thread pool with 5 threads where each one relinquishes the HW once it's done and adds it back to some queue. Would that work? If so, .Net makes thread pools very easy.


Sounds a lot like the Sleeping barber problem. I believe the standard solution to that is to use semaphores

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号