2011-10-04 36 views
2

我可以在任何時候同時運行最多5個線程,它使用5個獨立硬件來加速一些複雜計算的計算並返回結果。每個硬件的API(僅包含一種方法)不是線程安全的,只能在任何時間點在單個線程上運行。一旦計算完成,相同的線程可以重新用於根據可用性在相同或不同的硬件上啓動另一個計算。每個計算都是獨立的,不依賴於其他計算的結果。因此,最多5個線程可以按任意順序完成其執行。根據以下方案分配線程的最有效方法是什麼?

什麼是最高效的C#(使用.Net Framework 2.0)編碼解決方案,用於跟蹤哪些硬件是空閒/可用的,並將線程分配給適當的硬件API以執行計算?請注意,除了限制5個併發運行的線程外,我無法控制線程何時或如何被觸發。

如果我錯了,請更正我的錯誤,但無鎖解決方案是首選,因爲我相信這會提高效率和更具可擴展性的解決方案。

另外請注意,這不是功課雖然它聽起來像...

+0

你說的5個獨立的硬件是什麼意思?你想分發超過5臺機器? –

+0

我在想象他有5件事情,例如機載協處理器設備。也許他們是特殊的硬件加密設備。無論如何 - 他們會做一些計算密集的工作,比方說。而且他們每次只能做一件事。它們不是遠程通用計算機,所以這不是分佈式計算的問題。相反,它們是專用資源,由單個程序使用。 – Cheeso

回答

0

聽起來像是你需要用5個線程,其中每一個放棄硬件線程池一旦完成,並將其添加回一些隊列。這會起作用嗎?如果是這樣,.Net使線程池非常容易。

+0

您不需要創建包含5個線程的池。 .NET有一個線程池,它有N個線程,你通常不關心有多少線程。這裏重要的是5個資源。您需要5個可鎖定對象來保護5個資源。然後池中的任何線程都可以鎖定對象,使用它保護的資源,然後釋放對象。開始儘可能多的後臺工作任務,最多隻能同時運行5個。 – Cheeso

+0

我認爲這是一個特殊的情況,你喜歡線程的數量。爲什麼你會創建超過5個,如果有任何額外的線程肯定會阻止?獲得了什麼? –

+0

有沒有說。在鎖定硬件資源之前,可能會有線程完成一些工作。硬件資源上的工作完成後,線程可能會進行一些後處理。但是在資源鎖隊列上等待額外的線程幾乎沒有任何代價。這是一個簡單的抽象。 – Cheeso

1

無鎖解決方案只有在計算時間非常短時纔有用。

我會爲每個硬件線程創建一個門面,在每個硬件線程中,作業排入隊列並在每次作業結束時調用回調。

喜歡的東西:

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

不要創建線程。使用QUWI。 – Cheeso

+0

將我的回答移至您的問題,因爲它更適合您的問題。 – jgauffin

+0

使用像......這樣的鎖*什麼*?建議使用QUWI,除非您有特定的理由來管理您自己的線程。原因衆所周知,並在其他地方記錄。基本上:讓.NET爲你做;你的應用程序可能不太瞭解該機器,以便很好地處理線程調度和優化,而且不應該集中於這個問題。 – Cheeso

2

.NET提供,你可以使用一個線程池。 System.Threading.ThreadPool.QueueUserWorkItem()告訴池中的線程爲你做一些工作。

如果我設計這個,我不會專注於線程映射到您的硬件資源。相反,我會爲每個HW資源公開一個可鎖定的對象 - 這可以簡單地是一個數組或5個對象的隊列。然後,對於您的每一位計算,請撥打QueueUserWorkItem()。在傳遞給QUWI的方法中,找到下一個可用的可鎖定對象並將其鎖定(也就是說,將其解除鎖定)。使用HW資源,然後重新排列對象,退出QUWI方法。

無論你打電話給QUWI多少次,最多可鎖定5個鎖,每個鎖都可以訪問您的特殊硬件設備的一個實例。

doc page for Monitor.Enter()顯示瞭如何創建一個可以被多個工作人員訪問的安全(阻塞)隊列。在.NET 4.0中,你會使用the builtin BlockingCollection - 這是一回事。

這基本上是你想要的。除了不要撥打Thread.Create()。使用線程池。

舉: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(); 
} 
+0

使用這樣的鎖會阻塞每個想要訪問硬件的線程,直到它可以。因此,如果20個不同的線程想訪問相同的硬件,他們將全部阻塞,直到他們獲得訪問權。我的解決方案將接受一個工作請求,然後讓每個調用線程繼續進行其他工作。您也可能想詳細說明爲什麼在這種情況下使用專用線程是一件壞事,因爲五個線程不需要太多資源,但使代碼更易於跟蹤和調試。 – jgauffin

+0

'使用這樣的鎖會阻止每個想要訪問硬件的線程,直到它可以。「我認爲這是目標。一次只允許一個線程訪問每個硬件資源。 – Cheeso

+0

重點是'因此,如果20個不同的線程想要訪問相同的硬件,他們將全部阻止,直到他們獲得訪問權限。通過使用避免的專用線程。 – jgauffin

相關問題