使用Task构造函数时无法使用Result启动任务

时间:2014-10-11 23:08:09

标签: c# windows-runtime windows-phone windows-store-apps async-await

我有一堆任务,我希望能够按顺序运行。问题是,它们涉及大量的磁盘读取,我需要在两者之间进行一些磁盘读/写,所以我希望能够创建一堆从磁盘读取的任务(并返回结果),但在我准备好之前不要启动它们。

因此,我无法使用Task.RunTask.Factory.StartNew。我理解这是Task构造函数的用途。

例如:

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.

编辑:应该注意的是,它目前看起来有些奇怪。这是因为在读取文件的每个步骤中,我都会在返回数据之前对数据进行一些额外的逻辑处理。这不应该影响代码,只要我可以在Task构造函数方法中进行异步调用。

Edit2:似乎有人希望我对我的要求更清楚。

是否有一种方法可以创建具有返回值的任务,但不能启动它(可能使用Task<TResult> constructor)以便我可以启动它并等待另一个时间的值。目前我收到了上述例外情况。

2 个答案:

答案 0 :(得分:1)

我似乎已经弄明白了。基于this answer

似乎我需要保留外部Task的副本并单独调用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;
}

这确保了解包已完成并安排内部任务(但不启动它)。

答案 1 :(得分:0)

Task.RunTask.Factory.StartNewTask.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
}