Thursday, 13 January, 2005
Asynchronous I/O in .NET?
I was doing some speed testing today, trying to saturate a USB port by writing to multiple flash RAM drives at the same time. I took the opportunity to explore the asynchronous I/O support in the .NET Framework, and found out that it doesn't quite work as advertised. I could do asynchronous reads, but asynchronous writes to the USB drives would block. Indulging my curiousity, I decided to see if it worked on network drives and the local hard drive. I got the same results: asynchronous reads work as expected, but asynchronous writes block. At least, the writes block on network I/O. It's kind of difficult to determine if the writes block on the local drive, as the thing will write 100 megabytes almost instantly. Windows has a serious problem with writing larger blocks (it appears to depend on how much memory you have), but that's a topic for another day.
A few minutes with Google revealed the Microsoft Knowledge Base article Asynchronous Disk I/O Appears as Synchronous on Windows NT, Windows 2000, and Windows XP. The article explains the many reasons why asynchronous I/O might appear synchronous, and gives hints for resolving the problems. One of the primary reasons is caching. If the file is opened using caching, then for complicated reasons the cache can work against you and the I/O operation will proceed synchronously. This is an interesting case of an operating system optimization (caching) interfering with an application-level optimization (asynchronous I/O). The article explains how to get around the problem by turning off caching for the file, but doing so imposes some restrictions on the I/O buffer and the size of reads and writes. It doesn't appear that a .NET program can meet those requirements without resorting to the Windows API to open files and allocate memory.
Fortunately, there is a workaround in .NET--spawn a thread and have it do the I/O synchronously. By creating multiple threads, I was able to get back to my testing of the USB drives. But that project is going to take a back burner while I explore this asynchronous I/O oddity. The reason? Nobody's paying me to swamp my USB drive, but I can probably sell an article about how to do asynchronous I/O in .NET programs.