Reputation: 8578
I have a task that is waiting for a property to be set to true (= completed). The way I am receiving that property value change is via EventHandler (System.Diagnostics.Process.OutputDataReceived
to be exact - it continuously reads the output of another process until the correct output is provided). However checking for the property all the time feels somewhat inefficiencient. I have tried adding a small delay of one tick because I believe I can allow myself such a wait if that would save CPU time, but I read .NET struggles with fractional milliseconds. Can I improve this code?
private ConcurrentBag<string> _allMessages = new ConcurrentBag<string>();
public OutputRetriever()
{
var process = new System.Diagnostics.Process();
...
process.OutputDataReceived += OutputDataReceived;
process.Start();
}
public async Task<string[]> GetAllOutput()
{
while (!IsCompleted)
{
// how to properly wait here?
// await Task.Delay(TimeSpan.FromTicks(1)); // is this ok?
}
return _allMessages.ToArray();
}
private void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
{
_allMessages.Add(e?.Data);
if (e?.Data == "success")
{
IsCompleted = true;
}
}
Upvotes: 1
Views: 99
Reputation: 15197
The timers in Windows have a resolution of approx. 16 ms, so any delay below 16 ms cannot be precisely achieved. This applies to any timer - the .NET timers are just wrappers for Windows native timers.
Instead of busy-waiting in a loop, create a custom TaskCompletionSource<T>
and return a Task
that can be awaited.
class OutputRetriever
{
private readonly ConcurrentBag<string> _allMessages = new ConcurrentBag<string>();
private readonly TaskCompletionSource<string[]> _taskSource
= new TaskCompletionSource<string[]>();
// Note: this method is not async anymore
public Task<string[]> GetAllOutput()
{
// We just return a task that can be awaited
return _taskSource.Task;
}
void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
{
_allMessages.Add(e?.Data);
if (e?.Data == "success")
{
// Here we notify that the task is completed by setting the result
_taskSource.SetResult(_allMessages.ToArray());
}
}
}
Now the clients can simply await the results as usual:
var receiver = new OutputReceiver();
string[] messages = await receiver.GetAllOutput();
Upvotes: 1