Использование Ienumerable.TakeWhile, но возвращает только один набор результатов

1

Прежде всего, я хочу извиниться, если мой код плохой или плохое описание. Это один из моих первых случаев работы с потоками/задачами С#. То, что я пытаюсь сделать в своем коде, - это перечислить список имен и для каждого 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);
        }
    }
Теги:
multithreading

4 ответа

2
Лучший ответ

Реализуйте перечислимое число в список, чтобы он вычислялся один раз, а не каждая итерация в цикле. Вы можете использовать методы 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);
  }
}
0

это не прямое решение, но оно может работать.

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.

0

Имя TakeWhile предполагает, что он принимает только записи, пока условие истинно. Поэтому, если он начинается с чтения записи, для которой условие является ложным, оно никогда не принимает ничего.

Таким образом, первый цикл, вы начинаете с num1= 0. Таким образом, он считывает записи из num1 в num2.

Во втором цикле вы начинаете с num1 51. Так что он снова начинает чтение... и первая запись, на которую он попадает, условие ложно, поэтому оно останавливается.

Вы можете попробовать использовать " Where или " Skip перед рукой".

Tl; dr; из этого: я не думаю, что ваша проблема имеет какое-то отношение к параллельным задачам, я думаю, что это связано с использованием неправильного метода LINQ, чтобы вытащить имена, которые вы хотите использовать.

0

Насколько я понимаю из ответа Стивена Клири на аналогичный (хотя и не идентичный) вопрос, вам не нужно использовать ConfigureAwait() там.

Здесь ссылка на вопрос: переполнение стека

И вот что я сделал бы вместо двух последних строк цикла for:

Task.Factory.StartNew(() => getCurrentData(results));

Это. Используя фабрику и не ожидая, вы позволяете этой задаче запускаться самостоятельно (возможно, в новом потоке). При условии, что ваше хранилище полностью безопасно для потоков (см.: System.Collections.Concurrent btw), тогда вы должны быть настроены.

Предостережение: если вы не показываете нам, что лежит после ожидания, ваши результаты могут отличаться.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню