使用任务构造函数时无法启动带有结果的任务

问题描述 投票:0回答:2

我有很多任务希望能够按顺序运行。问题是,它们涉及大量的磁盘读取,我需要在使用每个磁盘之间进行一些磁盘读取/写入,所以我希望能够创建一堆任务来从磁盘读取(并返回结果),但在我准备好之前才开始。

因此,我无法使用

Task.Run
Task.Factory.StartNew
。据我了解,这就是
Task
构造函数的用途。

例如:

public async Task<IEnumerable<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
),以便我可以启动它并在其他时间等待该值。目前我收到了上面包含的异常。

c# windows-runtime windows-phone windows-store-apps async-await
2个回答
1
投票

我好像已经明白了。基于这个答案

看来我需要保留外部任务的副本并单独调用Task.Start,然后我可以按预期返回Unwrapped任务。

示例:

public async Task<IEnumerable<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-bound 任务。您的任务受 I/O 限制。

有没有一种方法可以让我创建一个带有返回值的任务,但不启动它......这样我就可以启动它并在其他时间等待该值。

当然;你想要的实际上是一个具有

async
兼容签名的委托。在这种情况下,一个
Func<Task<byte[]>>
。我的博客上有更多
async
兼容代表的示例

所以,你的例子可以是:

public async Task<IEnumerable<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 }
    
© www.soinside.com 2019 - 2024. All rights reserved.