What are the differences between AsyncEx.AsyncLock and Scott Hanselman's AsyncLock?
Asked Answered
M

1

1

I recently had the need to add an asynchronous variant of the lock keyword to one of my applications. There are many implementations to choose from, but the two that most appealed to me were:

What are the differences during runtime for the two solutions?
Of course the AsyncEx AsyncLock has more features, but assuming I don't need those, how do the two compare?

Makassar answered 26/2, 2023 at 8:55 Comment(2)
So the assumption is that you won't need to wait with CancellationToken support? Just a bare-bone WaitAsync with no arguments and nothing else?Demonize
@TheodorZoulias Yes.Makassar
D
3

AFAIK the AsyncLock component of the AsyncEx library was authored before the introduction of the SemaphoreSlim.WaitAsync API, and the same is also true for Stephen Toub's AsyncLock synchronization primitive. On the contrary Scott Hanselman's AsyncLock was published after the release of the .NET Framework 4.5 (October 2012), and so it is making use of the aforementioned API. It is actually a very thin and lightweight wrapper around this API.

My assumption is that all these components have the same runtime behavior. It is hard to say it with absolute confidence for the AsyncEx version, because the source code is quite complex, and it's not contained in a single code file.

My personal preference between those options would be to use none. I find using a SemaphoreSlim(1, 1) directly quite convenient, and I am not a fan of (ab)using the using statement for purposes other than releasing unmanaged resources.

await mutex.WaitAsync();
try
{
    //...
}
finally { mutex.Release(); }

It's not much more code than:

using (await mutex.LockAsync())
{
    //...
}
Demonize answered 26/2, 2023 at 23:9 Comment(12)
> (ab)using the using statement for purposes other than releasing unmanaged resources. This is not an abuse of using. Using just ensures Dispose() is called, which doesn't necessarily have anything to do with unmanaged resources.Nicholson
@Nicholson according to the documentation "The using statement ensures the correct use of an IDisposable instance". Also according to the documentation the IDisposable "Provides a mechanism for releasing unmanaged resources". I am not making this up. Releasing unmanaged resources is the stated intended purpose of this interface.Demonize
These are sort of halve-truths though. The using statement does ensure the correct use of an IDisposable instance, but from the language spec, it only ensures that .Dispose() is called via duck typing. There are many "disposable" things in the framework that don't implement IDisposable for performance reasons. The documentation for IDisposable is also notoriously bad. Only the IDisposable pattern (with finalizer) is employed for disposing unmanaged resources. In their own documentation they have an example Dispose method that does nothing but call Dispose on managed objects.Nicholson
@Nicholson having a public Dispose method is not enough. An object in using has to be implicitly convertible to IDisposable, otherwise the compiler throws a CS1674 error. Do you know of any class in the standard .NET libraries that implements the IDisposable, and the Dispose does something else than disposing unmanaged resources? I don't know of any. If you also don't know of any, then I think the case is closed: Using the using for other purposes is against the C#/.NET designers' intentions.Demonize
@Thordor Classes like StreamWriter feel like obvious examples. StreamWriter.Dispose() exists to flush managed buffers to the stream, and doesn't necessarily even call the underlying Stream.Dispose(). Usescases like this demonstrate Dispose() implementing managed cleanup work which has nothing to do with unmanaged resources, in the framework.Nicholson
@Nicholson good example. The StreamWriter derives from the TextWriter, and the TextWriter.Dispose is a virtual method that does nothing. My guess is that the .NET designers anticipated that at least some of the classes deriving from the TextWriter would hold unmanaged resources, and decided to make the base class IDisposable to clean them up. The documentation of the TextWriter.Dispose links to a document titled "Cleaning Up Unmanaged Resources". Do you have another example, that doesn't involve inheritance?Demonize
@Nicholson one egregious example of IDisposable misuse that I know of, is the ICacheEntry interface. The way you add a value in the MemoryCache is to call cache.CreateEntry, and then Dispose this ICacheEntry instance. WTF? The MemoryCache is equipped with convenience extensions methods that make this create-dispose pattern optional, but still... This API is not part of the .NET standard libraries though. I don't know who is the original author of this library. What was he thinking?Demonize
C# supports using ref structs that can't implement IDisposable. sharplab.io/…Bowery
IMO using has outgrown its roots surrounding unmanaged resources. It is cognitively helpful to be able to know that some clean-up code will be run at the end of the scope. This is heavily leveraged in C++ via the RAII pattern.Bowery
@NickStrupat thanks for the info about ref structs, I didn't know that. As I've learned just now, this is called pattern-based using. It was introduced in C# 8 (along with .NET Core 3.0, September 2019), because otherwise ref structs could not participate in using statements, because these types can't implement interfaces. Personally I'll have to see more evidence about the using having outgrown its roots surrounding unmanaged resources, before I am convinced.Demonize
There is no amount of evidence that can disprove the fact that MSDN says IDisposable is for unmanaged resources. That said, all of the uses of RAII that aren't about memory management are examples of how using statements can reduce the cognitive load when reading code.Bowery
@NickStrupat ​I am not aware what RAII is. Could you post a few links demonstrating creative uses of using? If one person uses something against its intended purpose, it's a misuse. If 10,000 persons do it, it's standard practice in the industry.Demonize

© 2022 - 2024 — McMap. All rights reserved.