Прежде всего, я хочу извиниться, если мой код плохой или плохое описание. Это один из моих первых случаев работы с потоками/задачами С#. То, что я пытаюсь сделать в своем коде, - это перечислить список имен и для каждого 50 имен в списке, запустить новую задачу и передать эти 50 имен другому методу, который будет выполнять вычисления тяжелых методов для данных. Мой код работает только для первых 50 имен в списке, и он возвращает 0 результатов за каждый другой раз, и я не могу понять, почему.
public static async void startInitialDownload(string value)
{
IEnumerable<string> names = await Helper.getNames(value, 0);
decimal multiple = names.Count() / 50;
string[] results;
int num1 = 0;
int num2 = 0;
for (int i = 0; i < multiple + 1; i++)
{
num1 = i * 50;
num2 = (50 * (i + 1));
results = names.TakeWhile((name, index) => index >= num1 && index < num2).ToArray();
Task current = Task.Factory.StartNew(() => getCurrentData(results));
await current.ConfigureAwait(false);
}
}
Реализуйте перечислимое число в список, чтобы он вычислялся один раз, а не каждая итерация в цикле. Вы можете использовать методы Skip
и Take
для получения диапазона списка:
public static async void startInitialDownload(string value) {
IEnumerable<string> names = await Helper.getNames(value, 0);
List<string> nameList = names.ToList();
for (int i = 0; i < nameList.Count; i += 50) {
string[] results = nameList.Skip(i).Take(50).ToArray();
Task current = Task.Factory.StartNew(() => getCurrentData(results));
await current.ConfigureAwait(false);
}
}
Или вы можете добавлять элементы в список и выполнять его, когда он имеет правильный размер:
public static async void startInitialDownload(string value) {
IEnumerable<string> names = await Helper.getNames(value, 0);
List<string> buffer = new List<string>();
foreach (string s in names) {
buffer.Add(s);
if (buffer.Count == 50) {
Task current = Task.Factory.StartNew(() => getCurrentData(buffer.ToArray()));
await current.ConfigureAwait(false);
buffer = new List<string>();
}
}
if (buffer.Count > 0) {
Task current = Task.Factory.StartNew(() => getCurrentData(buffer.ToArray()));
await current.ConfigureAwait(false);
}
}
это не прямое решение, но оно может работать.
public static IEnumerable<T[]> MakeBuckets<T>(IEnumerable<T> source, int maxSize)
{
List<T> currentBucket = new List<T>(maxSize);
foreach (var s in source)
{
currentBucket.Add(s);
if (currentBucket.Count >= maxSize)
{
yield return currentBucket.ToArray();
currentBucket = new List<T>(maxSize);
}
}
if(currentBucket.Any())
yield return currentBucket.ToArray();
}
позже вы можете выполнить итерацию по результату функции MakeBucket.
Имя TakeWhile
предполагает, что он принимает только записи, пока условие истинно. Поэтому, если он начинается с чтения записи, для которой условие является ложным, оно никогда не принимает ничего.
Таким образом, первый цикл, вы начинаете с num1
= 0. Таким образом, он считывает записи из num1
в num2
.
Во втором цикле вы начинаете с num1
51. Так что он снова начинает чтение... и первая запись, на которую он попадает, условие ложно, поэтому оно останавливается.
Вы можете попробовать использовать " Where
или " Skip
перед рукой".
Tl; dr; из этого: я не думаю, что ваша проблема имеет какое-то отношение к параллельным задачам, я думаю, что это связано с использованием неправильного метода LINQ, чтобы вытащить имена, которые вы хотите использовать.
Насколько я понимаю из ответа Стивена Клири на аналогичный (хотя и не идентичный) вопрос, вам не нужно использовать ConfigureAwait() там.
Здесь ссылка на вопрос: переполнение стека
И вот что я сделал бы вместо двух последних строк цикла for:
Task.Factory.StartNew(() => getCurrentData(results));
Это. Используя фабрику и не ожидая, вы позволяете этой задаче запускаться самостоятельно (возможно, в новом потоке). При условии, что ваше хранилище полностью безопасно для потоков (см.: System.Collections.Concurrent
btw), тогда вы должны быть настроены.
Предостережение: если вы не показываете нам, что лежит после ожидания, ваши результаты могут отличаться.