У меня есть программа, интенсивно использующая вычислительные ресурсы, которую я пытаюсь распараллелить, однако одним из предельных шагов является операция ввода-вывода, которая контролируется феноменально неэффективным API. Я не контролирую, но у меня нет выбора, кроме как использовать. Крайне важно, чтобы мое распараллеливание не увеличивало число операций ввода-вывода, или любая выгода, скорее всего, очень быстро исчезнет.Каково поведение ожидания внутри цикла Parallel.ForEach()?
Раскладка что-то вроде этого: У меня есть два класса, Foo
и Bar
, и для того, чтобы рассчитать Foo
, который не включает в себя не малое количество вычислений, я должен передать его экземпляр или несколько экземпляров, из Bar
которых Я импортирую из другого файла в чрезвычайно дорогостоящую операцию ввода-вывода. Я требую большого количества экземпляров как , так и Bar
. Многие из этих Bar
экземпляров будут использованы для расчета более чем одного экземпляра Foo
. В результате я не хочу отбрасывать свои экземпляры Bar
после того, как я вычислил каждый номер Foo
, и я не хочу импортировать их более одного раза. Потенциально замечать, что сложнее усложнять API, 32-разрядный, тогда как моя программа должна быть 64-битной, чтобы избежать MemoryException
, поэтому она обрабатывается локально размещенным сервером, с которым я общаюсь с использованием WCF.
Вот предложили мое решение, но я очень новый для распараллеливания и, в частности, я не уверен в том, как await
будет обрабатываться внутри WRT петли ForEach высвобождая процессоры:
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
, или процессор просто простаивает? Если я тогда устрою своего рода WaitAll()
за пределами Parallel.ForEach()
, смогу ли я распараллелить все это эффективно? Есть ли у кого-нибудь лучшие идеи о том, как я могу это реализовать?
Редактировать, чтобы обеспечить MCVE:
я не могу удовлетворить проверяемую составляющую этого, как я не могу дать вам API, и я, конечно, не могу дать вам какое-либо данных о том, что API может получить доступ, однако я попытаюсь чтобы предоставить вам что-то до вызова на сервер.
Программа может эффективно идти бесконечно глубоко в том, как она обрабатывает вещи, ее гораздо легче воспринимать как синтаксический анализатор конкретных инструкций, которые клиенту разрешено строить с помощью графического интерфейса и набора «кирпичей». Таким образом, 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()» и «ждать foo.CalculateStuff()», но я не уверен, что это правильная вещь. –
Вот какой контекст, почему следует избегать async void https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – rashleighp
Спасибо вам, да, предложение использовать «async Task» является хорошим и сделало бы обработку всего этого чище, когда я в конечном итоге обработаю его в методе 'Main()' Консоли, но он не отвечает на вопрос, на который я ответил. Мне не нужно «ждать» foo.CalculateStuff() ', потому что мне не нужен результат, прежде чем продолжить мой код, но опять же, это не имеет отношения к вопросу: процессор будет заблокирован' await' внутри 'CalculateStuff()' или будет цикл 'ForEach()' попробует другой 'Foo' и посмотреть, готов ли он' requiredBar' к доступу? – thepowerofnone