我有一個計算密集程序,我試圖並行化,但是其中一個限制步驟是I/O操作,它受控於一個效率非常低的API,我無法控制,但別無選擇,只能使用。我的並行化並不會增加I/O操作的數量,或者任何好處可能會很快消失。在Parallel.ForEach()循環內等待的行爲是什麼?
佈局是這樣的:我有兩個班,Foo
和Bar
,並且爲了計算Foo
,其中涉及的計算不小的量,我必須通過它的一個實例,或者少數情況下,的Bar
這我從一個非常昂貴的I/O操作中從其他文件導入。我需要大量的Foo
和Bar
實例,並且這些實例中的許多將用於計算多個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.
}
除非您正在製作事件處理程序,否則永遠不會使用用戶'async void'。你可以做'async foo => ...'然後讓'async public Task CalculateStuff()'和'await foo.CalculateStuff()',但我不確定這是否是正確的。 –
以下是爲什麼應該避免異步void的一些上下文https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – rashleighp
謝謝你們兩個,是的建議使用'異步任務'是一個很好的,並且當我最終在控制檯的Main()方法中處理它時,會處理所有這些更清晰的事情,但它並不回答我回答的問題。我不需要'等待foo。CalculateStuff()'因爲我在繼續使用我的代碼之前不需要結果,但是這與現在的問題無關:處理器是否會被'CalculateStuff()'中的'await'鎖定,或者將會'ForEach()'循環嘗試另一個'Foo'並查看它是否'requiredBar'準備好被訪問? – thepowerofnone