2

У меня есть набор задач, которые я хочу выполнить в порядке. Дело в том, что они связаны с большим количеством чтения диска, и мне нужно будет делать чтение/запись на диске между ними, поэтому я хотел бы иметь возможность создавать кучу задач для чтения с диска (и возвращать результат), но не запускать их, пока я не буду готов.Не удается запустить задачу с результатом при использовании конструктора задач

Из-за этого я не могу использовать Task.Run или Task.Factory.StartNew. Я понимаю, что для этого был конструктор Task.

Ex:

public async Task<IEnumberable<Task<byte[]>>> ReadAllFiles() 
{ 
    var folder = await ApplicationData.Current.LocalFolder; 
    var files = await folder.GetFilesAsync(); 
    var fileTasks = files.Select(
     file => new Task<Task<byte[]>>(
      async() => { 
       return (await FileIO.ReadBufferAsync(file)).ToArray(); 
      }).Unwrap()); 
    return fileTasks; 
} 

Тогда в моем вызывающем методе я могу пойти:

var readTasks = await ReadAllFiles(); 
foreach(var task in readTasks) 
{ 
    task.Start(); // Throws exception 
    var bytes = await task; // If the previous line is commented out, does not return 
    // Do other stuff 
} 

Есть ли способ сделать это? Прямо сейчас task.Start() выдает исключение: System.InvalidOperationException: Additional information: Start may not be called on a promise-style task..

Редактировать: Следует отметить, что в настоящее время есть некоторые странности. Это связано с тем, что на каждом этапе, таком как чтение файла, я делаю дополнительную логическую обработку данных перед их возвратом. Это не должно влиять на код, если я могу выполнять асинхронные вызовы в методе конструктора задач.

Edit2: Кажется, мне хотелось бы, чтобы я был более ясен с тем, что я прошу.

Есть ли способ создать задачу с возвращаемым значением, но не запускать ее (вероятно, используя Task<TResult> constructor), чтобы я мог начать ее и ждать значения в другое время. В настоящее время я получаю исключение, включенное выше.

+0

Почему вы пытаетесь использовать 'Start()' здесь в первую очередь? – svick

+0

Итак, когда вы используете 'Task.Run' или' Task.Factory.StartNew', он автоматически запускает задание, которое он создает. Когда вы создаете задачу с помощью конструктора 'Task', она не запускает ее.Это означает, что вы должны вызвать 'Task.Start()' для запуска задачи, иначе, если вы ее ждете, она никогда не вернется. –

+0

Да, но ваш 'ReadAllFiles()' возвращает ленивую последовательность, поэтому даже если вы использовали 'Task.Run()', 'Task' запускался бы только тогда, когда вы перебираете их в своем 'foreach'. – svick

ответ

1

Кажется, я понял. На основе this answer.

Кажется, что мне нужно сохранить копию внешней задачи и вызвать Task.Start на нее отдельно, после чего я могу вернуть Unwrapped задачу, как ожидалось. Пример:

public async Task<IEnumberable<Task<byte[]>>> ReadAllFiles() 
{ 
    var folder = await ApplicationData.Current.LocalFolder; 
    var files = await folder.GetFilesAsync(); 
    var fileTasks = files.Select(
     file => { 
      var wrappedTask = new Task<Task<byte[]>>(
      async() => { 
       return (await FileIO.ReadBufferAsync(file)).ToArray(); 
      }); 

      var unwrappedTask = wrappedTask.Unwrap(); 
      wrappedTask.Start(); 
      return unwrappedTask; 
     }); 
    return fileTasks; 
} 

Это обеспечивает завершение развертывания и планирование внутренней задачи (но не запускает ее).

0

Task.Run, Task.Factory.StartNew и Task.Task все предназначены для CPU переплете задач. Ваши задачи связаны с I/O-привязкой.

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

Конечно; то, что вы хотите, на самом деле является делегатом с async-совместимой подписью. В этом случае, Func<Task<byte[]>>. У меня есть еще несколько примеров async-совместимых делегатов on my blog.

Итак, ваш пример может быть:

public async Task<IEnumberable<Task<byte[]>>> ReadAllFiles() 
{ 
    var folder = await ApplicationData.Current.LocalFolder; 
    var files = await folder.GetFilesAsync(); 
    var fileReaders = files.Select(file => new Func<Task<byte[]>>(
     async() => await FileIO.ReadBufferAsync(file)).ToArray())); 
    return fileReaders; 
} 

var readers = await ReadAllFiles(); 
foreach(var func in readers) 
{ 
    var bytes = await func(); 
    // Do other stuff 
} 
Смежные вопросы