Since we expect to be reading frequently, and for us to often be reading when data is already available to be consumed, should SendLoopAsync
return ValueTask
rather than Task
, so that we can make it allocation-free?
// Caller
_ = Task.Factory.StartNew(_ => SendLoopAsync(cancellationToken), TaskCreationOptions.LongRunning, cancellationToken);
// Method
private async ValueTask SendLoopAsync(CancellationToken cancellationToken)
{
while (await _outputChannel.Reader.WaitToReadAsync(cancellationToken).ConfigureAwait(false))
{
while (_outputChannel.Reader.TryRead(out var message))
{
using (await _mutex.LockAsync(cancellationToken).ConfigureAwait(false))
{
await _clientWebSocket.SendAsync(message.Data.AsMemory(), message.MessageType, true, cancellationToken).ConfigureAwait(false);
}
}
}
}
No, there is no value at the SendLoopAsync
returning a ValueTask
instead of Task
. This method is invoked only once in your code. The impact of avoiding a single allocation of a tiny object is practically zero. You should consider using ValueTask
s for asynchronous methods that are invoked repeatedly in loops, especially in hot paths. That's not the case in the example presented in the question.
As a side note, invoking asynchronous methods with the Task.Factory.StartNew
+TaskCreationOptions.LongRunning
combination is pointless. The new Thread
that is going to be created, will have a very short life. It's gonna be terminated immediately when the code reaches the first await
of an incomplete awaitable inside the async method. Also you are getting back a nested Task<Task>
, which is tricky to handle. Using Task.Run
is preferable. You can read here the reasons why.
Also be aware that the Nito.AsyncEx.AsyncLock
class is not optimized for memory-efficiency. Lots of allocations are happening every time the lock is acquired. If you want a low-allocation synchronization primitive that can be acquired asynchronously, your best bet currently is probably to use a Channel<object>
instance, initialized with a single null
value: retrieve the value to enter, store it back to release.