使用FileStream.WriteAsync()时,再次调用该方法时会发生什么

时间:2015-02-20 06:48:56

标签: c# asynchronous async-await filestream

我正在录制视频流到磁盘。为此,我使用以下代码:

private async Task SaveToFile(IntPtr data, Int32 size)
{
    if (_FileStream == null) return;
    Byte[] buf = new Byte[size];
    Marshal.Copy(data, buf, 0, size);
    //dont await here - just continue
    _FileStream.WriteAsync(buf, 0, size);
}

到目前为止它似乎没有问题。我只想确认如果在上一次迭代完成之前调用此方法会发生什么。

您会注意到我没有等待WriteAsync()来电。我不确定这是否正确,但我使用的API声明在此方法中将操作保持在最低限度,以便不在API内部阻止回调。这似乎是要纠正的事情,只需将数据传递给流,然后立即返回。

有人可以确认如果上一次WriteAsync()来电还没有结束会发生什么,我又会再次致电WriteAsync()吗?或者我应该await WriteAsync()调用吗?

编辑: 我还没有使用上面的方法遇到异常,所以我想我的最后一个问题是在外部DLL回调中使用WriteAsync()时是否有任何影响? DLL是第三方Directshow组件。我无法验证它是如何在内部工作的,但它只是通过回调向我提供数据,我将其保存到文件流中。

3 个答案:

答案 0 :(得分:5)

异步重叠WriteAsync操作没有任何问题,只要它们写入文件的不同,非重叠段:

using System;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            var tempFile = System.IO.Path.GetTempFileName();
            Console.WriteLine(tempFile);

            var fs = new System.IO.FileStream(
                tempFile, 
                System.IO.FileMode.Create, 
                System.IO.FileAccess.ReadWrite, 
                System.IO.FileShare.ReadWrite, 
                bufferSize: 256,
                useAsync: true);

            fs.SetLength(8192);

            var buff1 = Enumerable.Repeat((byte)0, 2048).ToArray();
            var buff2 = Enumerable.Repeat((byte)0xFF, 2048).ToArray();

            try
            {
                fs.Seek(0, System.IO.SeekOrigin.Begin);
                var task1 = fs.WriteAsync(buff1, 0, buff1.Length);

                fs.Seek(buff1.Length, System.IO.SeekOrigin.Begin);
                var task2 = fs.WriteAsync(buff2, 0, buff2.Length);

                Task.WhenAll(task1, task2).Wait();
                Console.ReadLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}

在低级别,所有这些都归结为WriteFile API,非空LPOVERLAPPED lpOverlapped,这是Windows API中支持的方案。

如果写入重叠,您仍然不会看到错误,但写入将会竞争,最终结果可能无法预测。

答案 1 :(得分:3)

  

您会注意到我没有等待WriteAsync()调用。我不确定这是否正确,但我使用的API声明在此方法中将操作保持在最低限度,以便不在API内部阻止回调。

您还没有显示足够的代码让我们任何人知道您在这里做了什么。但是,很明显,您可能需要一些帮助才能确切了解async方法的工作原理。

特别是,使用await语句会阻止方法的执行。事实上,恰恰相反:它导致方法在那时返回。

它所做的另一件事是为等待任务设置一个延续,这样当该任务完成时,继续执行。在您的方法中,对WriteAsync()方法的调用是该方法的最后一件事,因此实际上没有任何延续。但是使用await会产生另一个重要影响:它允许您的方法返回的Task正确指示整个方法的完成,包括对{{1}的调用}。

现在的情况是,你的方法在调用WriteAsync()之后立即返回(即在写操作实际完成之前),看起来调用者已经完成了,即使写了操作尚未完成。

在您的示例中,您的方法会写入明显的类成员WriteAsync()。在第一次完成之前再次调用该方法肯定会很糟糕;我怀疑你会得到异常,但如果你同时执行它们,_FileStream不会以任何方式保证对文件的连贯写入。

这里最好的事情可能就是不要使用FileStream并在写入时容忍阻塞。如果写入中的阻塞确实对您正在使用的API有问题,那么您应该使用缓冲区队列和单独的异步操作序列来写入文件,而不是每次都调用async API要求您写入数据。

答案 2 :(得分:1)

FileStream不是线程安全的。从不同线程写入同一文件的两个写入将无法正常工作。

正如@Noseratio所指出的,从同一个线程重叠调用WriteAsync是可以的。

因此,如果您从不同的线程写入文件,则需要同步对文件的访问。

另外,我还会修改您的SaveToFile以返回任务,并且由于您未使用await,因此该方法也不需要async。< / p>

private Task SaveToFile(IntPtr data, Int32 size)
{
    if (_FileStream == null) return;
    Byte[] buf = new Byte[size];
    Marshal.Copy(data, buf, 0, size);
    //dont await here - just continue
    return _FileStream.WriteAsync(buf, 0, size);
}