Как сделать так, чтобы метод, вызываемый из taskList, не выполнялся одновременно? - PullRequest
0 голосов
/ 20 марта 2019

У меня есть метод, который загружает memoryStream из cloudDrive, а затем передает этот memoryStream в taskList, который читает файл:

  public async Task CreateFiles(List<MyDataClass> list)
    {
        var tasks = new List<Task>();

        foreach (var item in list)
        {
            var memorySteam = await _cloudService.DownloadStreamFromCloudAsync(item);

            tasks.Add(Task.Run(() =>
            {
                using (memoryStream)
                {
                    ReadFile readFile = new ReadFile(memoryStream, new MyFileSerializer());
                    readFile.DoWork();
                }
                UpdateTreeViewRecursively();

            }));
        }
    }

    public void UpdateTreeViewRecursively()
    {
        //...
        //Here I do recursive operations on a treeView. This may take a while
    }

Проблема в том, что, когда одна задача завершена и файл прочитан, я вызываю метод, который выполняет некоторую трудоемкую работу, и есть вероятность, что этот метод еще не завершен, и следующая задача уже вызывает его снова. Должен ли я использовать блокировку или есть другие решения этой проблемы?

EDIT: Я постараюсь объяснить, почему я выбрал такую ​​архитектуру:

1) Пользователь вызывает метод CreateFiles и начинается загрузка из облака: 30 файлов, размер которых варьируется от 1 до 70 МБ. Загрузка ограничена одним файлом за раз.

2) Затем каждый загруженный memoryStream передается в задачи. Добавить (..). Поступая так, я хотел бы добиться того, чтобы несколько файлов можно было читать одновременно, и когда один файл готов, я вызываю UpdateTreeViewRecursively (); Этот метод помечает treeViewItem как готовый к использованию, а также проверяет, все ли дочерние элементы его родителя загружены и готовы к использованию. Таким образом, пользователь сразу видит, какие файлы можно использовать, и ему не нужно ждать обработки больших файлов размером 70 Мб, чтобы начать работу с программой.

3) Чтобы убедиться, что флаги в treeView установлены правильно, мне нужно убедиться, что UpdateTreeViewRecursively (); метод не вызывается еще раз, пока его выполняет предыдущий вызывающий.

Ответы [ 2 ]

1 голос
/ 23 марта 2019

Проблема в том, что, когда одна задача завершена и файл прочитан, я вызываю метод, который выполняет некоторую трудоемкую работу, и есть вероятность, что этот метод еще не завершен, и следующая задача уже вызывает его снова. Должен ли я использовать блокировку или есть другие решения этой проблемы?

Поскольку этот метод изменяет поток пользовательского интерфейса , его следует запустить в потоке пользовательского интерфейса. Так что может быть что-то вроде этого:

public async Task CreateFiles(List<MyDataClass> list)
{
  var tasks = list.Select(async item =>
  {
    using (var memorySteam = await _cloudService.DownloadStreamFromCloudAsync(item))
    {
      await Task.Run(() =>
      {
        ReadFile readFile = new ReadFile(memoryStream, new MyFileSerializer());
        readFile.DoWork();
      });
    }

    UpdateTreeViewRecursively();
  });
}

Сам контекст пользовательского интерфейса действует как «блокировка», поэтому он не будет одновременно запускаться UpdateTreeViewRecursively.

Если код «Это может занять некоторое время» ухудшает взаимодействие с пользователем, вам следует реорганизовать этот метод, чтобы либо он мог разгрузить медленные части на Task.Run, либо использовать IProgress<T> для всех своих обновлений пользовательского интерфейса и запустить весь метод UpdateTreeViewRecursively в Task.Run. Примеры:

// Approach using Task.Run within UpdateTreeViewRecursively:
public async Task UpdateTreeViewRecursively()
{
  UpdateSomeTreeViewItem();
  var data = await Task.Run(() => SomeSlowCodeThatDoesNotUpdateUI());
  UpdateSomeOtherTreeViewItem(data);
}
// Approach using IProgress<T>:
public async Task CreateFiles(List<MyDataClass> list)
{
  var tasks = list.Select(async item =>
  {
    using (var memorySteam = await _cloudService.DownloadStreamFromCloudAsync(item))
    {
      await Task.Run(() =>
      {
        ReadFile readFile = new ReadFile(memoryStream, new MyFileSerializer());
        readFile.DoWork();
      });
    }

    var progress = new Progress<MyTreeViewItemUpdate>(update => UpdateSomeTreeViewItem(update));
    await Task.Run(() => UpdateTreeViewRecursively();
  });
}

public void UpdateTreeViewRecursively(IProgress<MyTreeViewItemUpdate> progress)
{
  progress?.Report(new MyTreeViewItemUpdate() { ... });
  var data = SomeSlowCodeThatDoesNotUpdateUI();
  progress?.Report(new MyTreeViewItemUpdate() { ... = data... });
}
0 голосов
/ 20 марта 2019

Либо используйте блокировки, либо используйте метод .Result для метода Task.Run, чтобы дождаться результата от задачи.Таким образом, ваш основной поток будет ждать, пока ваша задача не будет завершена.

...