OpenRead asynchronously
Asked Answered
I

3

12

I'm using the following C# code to read a tiny text file over a network share:

string fileContent;
using (var stream = File.OpenRead(filePath))
using (var reader = new StreamReader(stream, FileEncoding))
{
    fileContent = await reader.ReadToEndAsync();
}

Even though the text file is very small (less than 10 KB) this operation sometimes takes ~7 seconds to run. When that happens, I've noticed most of the time is spent on

File.OpenRead(filePath)

This is probably due to Windows having to resolve the file share and to acquire locks on the file over the network. As that method call is not asynchronous, this is blocking my current thread for several seconds.

Is there a safe way to read a file from disk asynchronously that also performs OpenRead asynchronously?

Implosive answered 2/4, 2015 at 1:32 Comment(1)
Have you tried copying or moving the file to local first and then reading it? Just try it if you haven't already to test it, that may show some other possible problem in network if that is the case.Kenric
F
18

No, unfortunately this is missing in the Win32 API. Device drivers do have the notion of an "asynchronous open", so the infrastructure is there; but the Win32 API does not expose this functionality.

Naturally, this means that .NET can't expose that functionality, either. You can, however, use a "fake asynchronous" operation - i.e., wrap your file read within a Task.Run, so that your UI thread isn't blocked. However, if you're on ASP.NET, then don't use Task.Run; just keep the (blocking) file open as it is.

Farquhar answered 2/4, 2015 at 1:47 Comment(6)
HOLY CRAP. Is there a voice to get that added to the Win 10 API?Bairam
I'm not on ASP.NET, but I would be interested in knowing why that would be a bad choice there specifically. Any references?Implosive
@aron: Not to my knowledge; feel free to open a UserVoice issue. I believe that async close is also possible, but may be more difficult to model.Farquhar
@roim: The main goal of async on a client UI is to free up the UI thread so the app is more responsive; it's acceptable (but not ideal) to use Task.Run, which just blocks a thread pool thread so the UI thread is not blocked.Farquhar
@roim: The main goal of async on ASP.NET is to free up thread pool threads so the server can scale; using Task.Run, would force a context switch and then use up one thread pool thread to free up another thread pool thread. A net loss. I've written about it a bit in an MSDN article and on my blog.Farquhar
I asked a follow up question based on your answer as the documentation for the 'Async' IO methods showed opening the filestream without specifying the useAsync parameter: #37042344Dustidustie
S
0

Much has changed in the last 8 years. The syntax to do what is asked in this question is as follows:

await using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 16384, FileOptions.Asynchronous);
using var reader = new StreamReader(stream);
string fileContent = await reader.ReadToEndAsync();

Doing this over a network may or may not be supported depending upon many factors but if you write your code this way then it could be async if Microsoft allows async access to the destination. Either way, it will work, even if the underlying SMB connection doesn't have an async API. It is no worse than what you were doing before IMO.

Sunflower answered 29/3 at 18:24 Comment(0)
B
-1
async Task<Stream> OpenReadAsync()
{      
    FileStream? s = null;

    await Task.Run(() => {
        s = File.OpenRead(FileName);});

    if (s == null) throw new Exception("Error opening file {FileName}");

    return s;
}
Borghese answered 12/9, 2023 at 23:59 Comment(2)
Thank you for contributing to the Stack Overflow community. This may be a correct answer, but it’d be really useful to provide additional explanation of your code so developers can understand your reasoning. This is especially useful for new developers who aren’t as familiar with the syntax or struggling to understand the concepts. Would you kindly edit your answer to include additional details for the benefit of the community?Ubiquitarian
The if (s == null) condition will never be true, so this if statement is redundant. Actually the whole OpenReadAsync method is redundant, because it violates the guideline about not exposing asynchronous wrappers for synchronous methods. You can just do FileStream s = await Task.Run(() => File.OpenRead(fileName)); directly at the location where you want to open the file.Hasidism

© 2022 - 2024 — McMap. All rights reserved.