2015-04-22 50 views
4

我有一個計算密集程序,我試圖並行化,但是其中一個限制步驟是I/O操作,它受控於一個效率非常低的API,我無法控制,但別無選擇,只能使用。我的並行化並不會增加I/O操作的數量,或者任何好處可能會很快消失。在Parallel.ForEach()循環內等待的行爲是什麼?

佈局是這樣的:我有兩個班,FooBar,並且爲了計算Foo,其中涉及的計算不小的量,我必須通過它的一個實例,或者少數情況下,的Bar這我從一個非常昂貴的I/O操作中從其他文件導入。我需要大量的FooBar實例,並且這些實例中的許多將用於計算多個Foo實例。因此,在計算每個Foo後,我不想放棄我的Bar實例,並且我不想每次輸入它們超過一次。可能需要注意的是,爲了使事情更加複雜,API是32位的,而我的程序必須是64位的,以避免MemoryException,所以這是由本地託管的服務器處理的,我使用WCF進行通信。

這是我提出的解決方案,但我非常新的並行化,特別是我不確定如何await會釋放處理器foreach循環WRT的內部處理:

ConcurrentDictionary<string, Task<Bar>> barList = new ConcurrentDictionary<string, Task<Bar>>(); 

Parallel.ForEach(fooList, foo => 
{ 
    if (!barList.ContainsKey(this.RequiredBarName)) 
    { 
     Task<Bar> importBar = Task.Run(() => Import.BarByName(this.RequiredBarName)); 
     barList.Add(this.RequiredBarName,importBar); 
    } 
    this.RequiredBarTask = barList.TryGetValue(this.RequiredBarName); 
    foo.CalculateStuff(); 
} 

// where foo.CalculateStuff() looks something like this 
async public void CalculateStuff() 
{ 
    // do some stuff... 
    Bar requiredBar = await this.RequiredBarTask; 
    // do some more stuff with requiredBar 
} 

會發生什麼當代碼遇到那個await? ThreadPool會採用不同的Task,還是處理器會閒置?如果我然後在Parallel.ForEach()之外安排某種WaitAll(),我能否通過所有這些有效地並行化?有沒有人有任何更好的想法,我可以如何實現這一點?

編輯提供MCVE:

我不能滿足這個可驗證的組件,因爲我不能給你的API,我當然不能給你任何的API可以訪問數據,但我會嘗試爲您提供一些直至呼叫服務器的信息。

該程序可以有效地處理事物的方式無限深,它可以更容易地被認爲是客戶端允許使用GUI構建的一組「磚塊」的特定指令的解析器。通過這種方式Dataflow看起來可以提供一個體面的解決方案。

在這個例子中,我沒有注意循環引用或者一個Channel計算另一個Channel,這已被Parallel.ForEach()方法調用;在我的代碼中,這是由一些邏輯和併發列表來處理,以檢查什麼時候被調用。當代碼運行到等待着

public abstract class Class 
{ 
    public string Name {get;set;} 
    public float[] Data {get;set;} 

    async public Task CalculateData(IsampleService proxy){} 
} 

public class Channel : Class 
{ 
    public Class[] ChildClasses {get;set;} 

    async public override Task CalculateData(IsampleService proxy) 
    { 
     foreach(Class childClass in ChildClasses) 
     { 
      // not the real processing but this step could be anything. There is a class to handle what happens here, but it is unnecessary for this post. 
      if(childClass.Data==null) await childClass.CalculateData(proxy); 
      this.Data = childClass.Data; 
     } 
    } 
} 

public class Input : Class 
{ 
    async public override Task CalculateData(IsampleService proxy) 
    { 
      this.Data = await proxy.ReturnData(this.Name); 
    } 
} 

async public static Task ProcessDataForExport(Channel[] channelArray) 
{ 
ChannelFactory<IsampleService> factory = new ChannelFactory<IsampleService>(new NetNamedPipeBinding(), new EndpointAddress(baseAddress)); 

IsampleService proxy = factory.CreateChannel(); 

Parallel.ForEach(channelArray, channel => 
    { 
     channel.CalculateData(); 
    }); 
// Task.WhenAll() might be a better alternative to the Parallel.ForEach() here. 
} 
+2

除非您正在製作事件處理程序,否則永遠不會使用用戶'async void'。你可以做'async foo => ...'然後讓'async public Task CalculateStuff()'和'await foo.CalculateStuff()',但我不確定這是否是正確的。 –

+1

以下是爲什麼應該避免異步void的一些上下文https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – rashleighp

+0

謝謝你們兩個,是的建議使用'異步任務'是一個很好的,並且當我最終在控制檯的Main()方法中處理它時,會處理所有這些更清晰的事情,但它並不回答我回答的問題。我不需要'等待foo。CalculateStuff()'因爲我在繼續使用我的代碼之前不需要結果,但是這與現在的問題無關:處理器是否會被'CalculateStuff()'中的'await'鎖定,或者將會'ForEach()'循環嘗試另一個'Foo'並查看它是否'requiredBar'準備好被訪問? – thepowerofnone

回答

2

會發生什麼?

,對於任何await聲明發生同樣的事情:具有評估任何表達式或語句檢索要等待的Task後,該方法將返回。對於所有意圖和目的,是方法的結尾。

ThreadPool會選擇不同的任務,還是處理器只是閒置?

這取決於還在發生什麼。例如,你在等什麼?如果它是排隊等待線程池的計算任務,並且尚未分配線程池線程,那麼確定線程池可能會選擇它並開始處理它。

如果您正在等待I/O操作,那麼這不一定會使處理器保持繁忙狀態,但線程池隊列中可能還有其他任務(例如來自Parallel.ForEach()調用的其他任務)。所以這會給處理器一些工作。

當然,使用await通常不會導致處理器閒置。實際上,使用它的主要原因是爲了避免(*)。當await語句導致當前方法返回時,您讓當前線程繼續進行,這意味着如果沒有足夠的線程來保持處理器繁忙,現在它有一些事情要做。 :)

(*)(好吧,有點......真的,主要原因是爲了避免阻塞當前線程,但這有副作用,有更多的工作可供處理器處理:))

如果我然後在Parallel.ForEach()之外安排某種WaitAll(),我能夠通過所有這些有效地並行化嗎?有沒有人有任何更好的想法,我可以如何實現這一點?

我在您的問題中沒有看到足夠的有用細節來回答這個問題。坦率地說,雖然我不能把它放在手指上,但使用代表的await似乎對我有點腥意。只要您致電await,代表的方法將返回。

因此,據Parallel.ForEach()知道,您在枚舉中完成該項目,但當然你不是。它將不得不在其他地方完成。最起碼,這似乎阻礙了班級充分了解其最有效安排工作的能力。

但也許沒關係。或者,這可能不是很好,但是考慮到你綁定的框架,你會取得最好的成績。很難說。


我的確鼓勵你提供Scott評論員所要求的MCVE。如果他是正確的,並且可以通過數據流API解決問題,那麼最好給他一個提供答案的機會。