【发布时间】:2014-03-11 21:54:16
【问题描述】:
我有一个繁重的 IO/CPU 转换“过程”,从文件类型 A 转换为 B。目前我将其写入 StorageFile,然后在转换完成后使用 BackgroundUploader 上传。
但是,我想尽快开始流式传输,同时仍在生成输出文件。此外,我什至不一定要创建输出 StorageFile,而是“随时随地”上传。
注意,我不知道转换过程中输出文件的最终大小,它可能比源文件小或大。
首先,我尝试在写入时简单地打开接收器输出 StorageFile,并将该流传递给 BackgroundUploader,但这会导致“竞争条件”,即上传在到达写入的字节末尾时终止StorageFile(当它赶上转换工作时)。
除了流式传输到 StorageFile,我还可以将输出字节写入缓冲区,例如 2KiB。我想在转换写入后上传这个缓冲区。
//simplified code...
uint bytesRead = 0;
byte[] buffer = new byte[buff_sz];
bytesRead = converter.Read(buffer);
while(bytesRead > 0)
{
// here I would like to 'upload' the data (maybe using BackgroundUploader? or some other API?)
bytesRead = converter.Read(buffer);
}
如果不遇到与以前相同的“竞争条件”,我不确定如何做到这一点。在将新字节放入缓冲区之前,如何保持 BackgroundUploader 运行?
注意 1:最后一次迭代将是 bytesRead
注意 2:转换代码位于跨平台 C++ 共享库中。
谢谢!
补充
根据 Nate Diamond 的建议,我研究了 IINputStream 接口。下面的代码可以工作,虽然可能是您编写的性能最差的版本,但足以证明这个概念。
以下基于“maxim pg”实现的包装代码从这里开始。 How can I implement IRandomAccessStream in C#?
public IAsyncOperationWithProgress<IBuffer, UInt32> ReadAsync(
IBuffer buffer,// The buffer into which the asynchronous read operation places the bytes that are read.
uint count,// The number of bytes to read that is less than or equal to the Capacity value.
InputStreamOptions options) // Specifies the type of the asynchronous read operation.
{
if (buffer == null) throw new ArgumentNullException("buffer");
Func<CancellationToken, IProgress<uint>, Task<IBuffer>> taskProvider =
(token, progress) => ReadBytesAsync(buffer, count, token, progress, options);
return AsyncInfo.Run(taskProvider);
}
private Task<IBuffer> ReadBytesAsync(
IBuffer buffer,
uint count,
CancellationToken token,
IProgress<uint> progress,
InputStreamOptions options)
{
TaskCompletionSource<IBuffer> cts = new TaskCompletionSource<IBuffer>();
try
{
var ignore = ThreadPool.RunAsync((handler) => {
_buffer = new byte[count];
uint bytesRead = _reader.Read(_buffer); // this triggers file conversion work
buffer.Length = bytesRead; // this is important apparently, otherwise no data written!
Stream stream = buffer.AsStream();
stream.Write(_buffer, 0, (int)bytesRead);
stream.Flush();
cts.TrySetResult(buffer);
});
}
catch(Exception e)
{
cts.SetException(e);
}
return cts.Task;
}
【问题讨论】:
-
所以,我注意到的一件事是 IInputStream(
BackgroundUploader.CreateUploadFromStreamAsync接受的内容)是一个简单的接口。它有一个你必须重写的方法。我在想的是,您可以创建自己的CustomInputStream类,该类接受输入并不断允许读取直到关闭。 Others seam to have had success with similar concepts. -
不过,我不确定这是否真的必要。您介意发布您以前尝试过但导致竞争条件的代码吗?就像,您可以制作一个简单的
InMemoryRandomAccessStream而不将其写入文件并使用它吗? (为此目的,它有一个GetInputStreamAt方法)。 -
感谢@NateDiamond,我听取了您关于实现 IInputStream 接口的建议。这并不完全直截了当,尤其是对于像我这样的 TAP 菜鸟。我会将我正在使用的代码添加到我的问题中,以防有人关心。请提交答案,我会投票。
标签: c# windows-store-apps