简体   繁体   English

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

[英]When using FileStream.WriteAsync() what happens when the method is called again

I am recording a video stream to disk. 我正在录制视频流到磁盘。 For this I use the following code: 为此,我使用以下代码:

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);
}

So far it seems to work without issue. 到目前为止它似乎没有问题。 What I just want to confirm is what happens if this method is called before the previous iteration has finished. 我只想确认如果在上一次迭代完成之前调用此方法会发生什么。

You will notice I don't await on the WriteAsync() call. 您会注意到我没有等待WriteAsync()调用。 I'm not sure if this is correct, but the API I am using states to keep operations to a minimum inside this method, so as to not block the callback internally in the API. 我不确定这是否正确,但我使用的API声明在此方法中将操作保持在最低限度,以便不在API内部阻止回调。 This seems like to correct thing to do, simply pass the data to the stream, and return immediately. 这似乎是要纠正的事情,只需将数据传递给流,然后立即返回。

Can someone please confirm what happens if the last WriteAsync() call hasn't finished, and I call WriteAsync() again? 有人可以确认如果最后一次WriteAsync()调用还没有完成会发生什么,我再次调用WriteAsync()吗? Or should I be await ing the WriteAsync() call? 或者我应该await WriteAsync()调用吗?

EDIT: I am yet to experience exceptions using the above method, so I guess my last question is are there any repercussions when using WriteAsync() from inside an external DLL callback? 编辑:我还没有使用上述方法遇到异常,所以我想我的最后一个问题是在外部DLL回调中使用WriteAsync()时是否有任何影响? The DLL is a third party Directshow component. DLL是第三方Directshow组件。 I cannot verify how it internally works, but it simply provides me with data via a callback, in which i save to the filestream. 我无法验证它是如何在内部工作的,但它只是通过回调向我提供数据,我将其保存到文件流中。

There is nothing wrong with asynchronous overlapping WriteAsync operations, as long as they write to different, non-overlapping segments of the file: 异步重叠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);
            }
        }
    }
}

On the low level, it all comes down to WriteFile API with non-null LPOVERLAPPED lpOverlapped , which is a supported scenario in Windows API. 在低级别,所有这些都归结为具有非空LPOVERLAPPED lpOverlapped WriteFile API,这是Windows API中支持的方案。

If the writes do overlap, you still won't see an error, but the writes will race and the end result may be unpredictable. 如果写入重叠,您仍然不会看到错误,但写入将会竞争,最终结果可能无法预测。

You will notice I don't await on the WriteAsync() call. 您会注意到我没有等待WriteAsync()调用。 I'm not sure if this is correct, but the API I am using states to keep operations to a minimum inside this method, so as to not block the callback internally in the API. 我不确定这是否正确,但我使用的API声明在此方法中将操作保持在最低限度,以便不在API内部阻止回调。

You haven't shown enough code for any of us to know exactly what you're doing here. 你没有显示足够的代码让我们任何人知道你在这里做了什么。 However, it's clear you probably need some help understanding exactly how async methods work. 但是,很明显,您可能需要一些帮助才能确切了解async方法的工作原理。

In particular, using the await statement does not block the method's execution. 尤其是,使用await语句阻止方法的执行。 In fact, quite the opposite: it causes the method to return at that point. 事实上,恰恰相反:它导致方法在那时返回。

The other thing that it does is set up a continuation for the awaited task, so that when that task does complete, the continuation is executed. 它所做的另一件事是为等待任务设置一个延续,以便当该任务完成时,执行继续。 In your method, the call to the WriteAsync() method is the last thing the method does, so there's not really any continuation. 在您的方法中,对WriteAsync()方法的调用是该方法的最后一件事,因此实际上没有任何延续。 But using await would have a another important effect: it allows the Task returned by your method to correctly indicate completion of the entire method, including the call to WriteAsync() . 但是,使用await将有另一个重要的作用:它允许Task你的方法回到正确显示整个方法完成,包括调用WriteAsync()

As things stand now, your method returns immediately after calling WriteAsync() (ie before the write operation actually completes), and it appears to the caller to have completed, even though the write operation hasn't necessarily completed yet. 现在的情况是,你的方法在调用WriteAsync()之后立即返回(即在写操作实际完成之前), 并且调用者看起来已经完成,即使写操作还没有完成。

In your example, your method writes to an apparent class member _FileStream . 在您的示例中,您的方法将写入明显的类成员_FileStream Calling the method again before it's completed the first time would definitely be bad; 在第一次完成之前再次调用该方法肯定会很糟糕; I doubt you'd get exceptions, but FileStream doesn't in any way guarantee coherent writes to the file if you execute them concurrently. 我怀疑你会得到异常,但是如果你同时执行它们, FileStream绝不会保证对文件的连贯写入。

Probably the best thing here is to just not use async and tolerate blocking on the write. 这里最好的事情可能就是不要在写入时使用async和容忍阻塞。 If blocking on the write really does turn out to be a problem for the API you are using, then you should use a queue of buffers and a separate sequence of asynchronous operations to write to the file instead of calling WriteAsync() every time the API calls you to write data. 如果写入中的阻塞确实对您正在使用的API有问题,那么您应该使用缓冲区队列和单独的异步操作序列来写入文件,而不是每次API调用WriteAsync()叫你写数据。

FileStream is not thread safe. FileStream不是线程安全的。 Two writes writing to the same file from different threads will not work correctly. 从不同线程写入同一文件的两个写入将无法正常工作。

As pointed out by @Noseratio making overlapping calls to WriteAsync from the same thread is fine. 正如@Noseratio指出的那样,从同一个线程WriteAsyncWriteAsync调用很好。

So if you are writing to the file from different threads, you need to synchronize access to the file. 因此,如果从不同的线程写入文件,则需要同步对文件的访问。

On a side note, I would aslo modify your SaveToFile to return the task, and because you are not using await the method doesn't need to be async either. 另外,我会修改你的SaveToFile以返回任务,因为你没有使用await所以方法也不需要是async

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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM