C# 用FileStream.WriteAsync 异步读文件 调用线程还是被阻塞了。 20C

在用FileStream WriteAsync方法(BeginWrite方法也是一样)的异步写文件时、
如下代码。实际上调用这段代码的时候,还是被阻塞了。

 FileStream stream = new FileStream("in.txt", FileMode.OpenOrCreate,
                                    FileAccess.ReadWrite, FileShare.ReadWrite, 1024, FileOptions.Asynchronous);
            byte[] byteData = new byte[80961024 * 4];
            Task task = stream.WriteAsync(byteData, 0, byteData.Length);

如果把这个FileStream绑定到一个StreamWriter上,用StreamWriter的WriteAsync方法倒是能异步,不会阻塞。

FileStream stream = new FileStream("in.txt", FileMode.OpenOrCreate,
                                    FileAccess.ReadWrite, FileShare.ReadWrite, 1024, FileOptions.Asynchronous);
 char[] byteData = new char[80961024 * 4];

            var sw = new StreamWriter(stream);

            Task task = sw.WriteAsync(byteData, 0, byteData.Length);

求大神解惑

c#

5个回答

估计是没有绑在一起同步造成的
AngOn823
姜团长 FileStream 也是可以往磁盘里写文件的啊,FileStream是以字节流的形式,StreamWriter是以字符的形式,字符比较直观,所以大家用的后者比较多吧
接近 2 年之前 回复
AngOn823
姜团长 哥们。你是发错了还是来打广告了。
接近 2 年之前 回复

StreamWriter是专门独立的一个文件流写入类,filestream的异步写入可能并不是以流的形势写入。

AngOn823
姜团长 不是以流的形式写入有什么后果吗?
接近 2 年之前 回复

又做了一个测试,把FileStream构造函数中的 buffersize大小由 1024 改成 80961024 * 4 也就是 byteData.Length 。这样看起来没有阻塞,而且
加上await之后也是立即就返回了结果,基本上是秒写。

小弟猜测这样实际是直接一次就把byteData 里面的内容复制到 stream 的缓冲区了,这些应该都是在内存里的操作,所以速度很快。而FileStream的WriteAsync函数是当 byteData里的东西全到它的缓冲区了就返回,从缓冲区到磁盘里的操作不用你在关心了,它自己解决。

小弟也不知道 write 的操作到底是具体是怎样,猜测是 当缓冲区小于要写的文件大小时,WriteAsync 应该是要一边从内存byteData里拿(不这样还能怎么办?),一边往磁盘里写的。如果这个操作确实是在另一个线程(或者DMA不占CPU)执行的话,又怎么会把调用它的线程阻塞死呢?

完整代码如下:await之后函数本应该立即返回到Mai函数中,但是并没有。

 static void Main(string[] args)
{
        ThreadPool.SetMaxThreads(1000, 1000);
        ThreadPoolMessage("Main");
        WriteAsync();
        ThreadPoolMessage("something");

        Console.ReadKey();
}

static async void WriteAsync()
{
        FileStream stream = new FileStream("out.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 1024 , FileOptions.Asynchronous);
        ThreadPoolMessage("begin");

        //char[] charData = new char[1024 * 1024 * 50];
        //var sw = new StreamWriter(stream);
        //await sw.WriteAsync(charData, 0, charData.Length);

        byte[] byteData = new byte[1024 * 1024 * 500];
        await stream.WriteAsync(byteData, 0, byteData.Length);

        ThreadPoolMessage("end");
        for (int i = 0; i < 10; i++) Console.WriteLine(i);
}

static void ThreadPoolMessage(string data)
{
        int a, b;
        ThreadPool.GetAvailableThreads(out a, out b);
        string message = string.Format("{0}\n  CurrentThreadId is {1}\n  " +
                                "WorkerThreads is:{2}  CompletionPortThreads is :{3}",
                                data, Thread.CurrentThread.ManagedThreadId, a.ToString(), b.ToString());
        Console.WriteLine(message);
}
weixin_37170955
ffffffffffffffff33 VS版本:Visual Studio 2017, 不同版本可能略有差异, 但不会太大!
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 http://thinkershare.com/storage/1.jpg
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 http://thinkershare.com/storage/2.jpg
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 我下面有截图
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 调试->符号:勾上Microsoft 符号服务器,自己注意修改符号位置
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 回复姜团长: 工具(菜单栏)->选项->调试->常规->启用.NET Framework源代码单步执行
接近 2 年之前 回复
AngOn823
姜团长 回复thinkershrae: 拷贝到哪去? 是一边往磁盘写,还是不停的通过1024的buffer拷贝到另外一个缓存里(它专门的io缓存区?)
接近 2 年之前 回复
AngOn823
姜团长 回复thinkershrae: 怎么在vs中调试net的源代码啊,把你发的这个.cs复制到vs里面吗
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 FileStream.Window.cs的997行开始有说明buffer尺寸的影响
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 你也可以在VS中调试.net framework源代码, 然后单步执行,很快就能找到原因了, 这个官方文档上的确没有说明
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 地址:http://referencesource.microsoft.com/#mscorlib/system/io/filestream.csdaa467db4989809e,references
接近 2 年之前 回复
weixin_37170955
ffffffffffffffff33 你的猜测是对的,请使用符号表查看源代码,缓冲区不够的时候会进行多次同步拷贝!
接近 2 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!