Last active
September 19, 2018 09:55
-
-
Save idg10/85760956b44c254b432e9aa69941ecf0 to your computer and use it in GitHub Desktop.
Resource handling with combined enumerable and Task-based deferred execution, ensuring correct sequencing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static IEnumerable<Task<int>> ParseFile(string path) | |
{ | |
using (var reader = new StreamReader(path)) | |
{ | |
while (!reader.EndOfStream) | |
{ | |
Task<int> readLineTask = null; | |
try | |
{ | |
readLineTask = ReadAndParseAsync(reader); | |
yield return readLineTask; | |
} | |
finally | |
{ | |
// There are three ways we can end up here: | |
// 1) an exception occurred (in which case readLineTask may still be null) | |
// 2) The caller asked for the next item (in which case execution will resume | |
// from the yield return, and we'll try another loop iteration | |
// 3) The caller has decided to stop early, calling Dispose on the enumerator | |
// before reaching the end | |
// If it's either 2 or 3 (i.e., we have a non-null readLineTask), we need to ensure | |
// that the last task we created is complete. (In case 2, we need it to be complete | |
// because we can't test for EndOfStream, nor can we go on to begin another read until | |
// the last read finishes. In case 3, we need to ensure that the read completes before | |
// we dispose the StreamReader.) | |
// Hopefully the caller already awaited the task for us. But if they didn't we need to | |
// block now until it completes. | |
if (readLineTask?.IsCompleted == false) | |
{ | |
// Calling Wait is almost always a terrible idea. But we've been | |
// forced into this by the mismatch between our return type, and the | |
// nature of the work we're doing. | |
readLineTask.Wait(); | |
} | |
} | |
} | |
} | |
} | |
private static async Task<int> ReadAndParseAsync(StreamReader reader) | |
{ | |
string line = await reader.ReadLineAsync().ConfigureAwait(false); | |
return int.Parse(line); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment