2

I have an asnyc method which returns a task and is doing some cleanup work in a finally block:

public Task<byte[]> MyFunction() {
   string filename = CreateFile();
   try {
      return File.ReadAllBytesAsync(filename);
   } finally {
      File.Delete(filename);
   }
}

Is it guaranteed, that the finally block will only be called after the return value has been awaited, or can this create a race condition where the file gets deleted before it is completely read?

mat
  • 1,645
  • 15
  • 36

2 Answers2

4

There is no guarantee that the file will not be created. You need to await the call (notice also the async keyword):

public async Task<byte[]> MyFunction() {
   string filename;
   try {
      filename = CreateFile();
      return await File.ReadAllBytesAsync(filename);
   } finally {
      File.Delete(filename);
   }
}

If the call is not awaited, then you return while still running the task and it will most likely not complete before the file is deleted. This would definitely be a race condition.

SzybkiDanny
  • 204
  • 2
  • 11
  • You were right regarding the order of events - I deleted my answer. – Mo B. Dec 05 '20 at 16:49
  • @SzybkiDanny The `CreateFile()` method is synchronous. If I am awaiting the ReadAllBytesASync command, am I not returning a byte[] right away instead of a Task? – mat Dec 05 '20 at 16:57
  • You return a _completed_ Task with byte[] result. – SzybkiDanny Dec 05 '20 at 17:08
  • What is the difference between doing this and just returning the `byte[]` synchronously? – mat Dec 05 '20 at 21:29
  • 1
    You don't really have much choice. If you want the code to work properly, then you need to `await` the call to async method (`ReadAllBytesAsync`). To be able to await the call, you have to mark the calling method (`MyFunction`) as `async`. Async methods can only return: `void`, `Task` and `Task`. If you want to return a result, then you need to go with `Task`. This is how async works. If you want to return `byte[]`, then you need to run the code synchronously but this implicates blocking of the current thread during IO operations execution. – SzybkiDanny Dec 05 '20 at 21:43
  • 1
    I can sense a bit of confusion about the point of running async methods. In general, it saves you from blocking the current thread during IO operations. I recommend reading [Stephen Cleary's blog](https://blog.stephencleary.com/2012/02/async-and-await.html) on it to get more info about the benefits, inner workings and gotchas. – SzybkiDanny Dec 05 '20 at 21:57
3

Not only will it not await anything, it won't compile, either. You need to take a few steps:

  1. Make the method async
  2. Actually await the result of File.ReadAllBytesAsync
  3. Declare the filename variable outside of the try-finally block
Riwen
  • 4,734
  • 2
  • 19
  • 31
  • I fixed your comment #3, thanks. Apart from that it does compile and work so far as expected. I do not actually want to await the Read call if I can help it but have it awaited outside on the return value if possible. – mat Dec 05 '20 at 17:22
  • @mat why don't you want to await the `File.ReadAllBytesAsync` call? – Theodor Zoulias Dec 05 '20 at 17:43
  • @TheodorZoulias: Because the async reading would be rendered mostly pointless, if I am waiting for the completion anyway in the very next line, if I understood things correctly. – mat Dec 05 '20 at 21:31
  • 1
    @mat awaiting an asynchronous operation is not pointless at all. It means that the current workflow has nothing to do while the operation is running, and so the current thread can be released and become available for doing other things. That's the whole purpose of asynchronous programming: to make it possible to create workflows that include high-latency operations, without blocking threads. Check [this](https://stackoverflow.com/questions/4844637/what-is-the-difference-between-concurrency-parallelism-and-asynchronous-methods) out for more details. – Theodor Zoulias Dec 06 '20 at 01:31