A new lock type in .NET 9

27/12/2023
.NET 9.NET

There is a new sheriff in town when it comes to the lock keyword, And that is the new System.Threading.Lock type that is introduced in .NET 9. And yes, I know - we still need time to digest the big .NET 8 release.

What is the System.Threading.Lock type?

Small disclaimer at the beginning: This feature/type is still in preview mode. You can download daily builds of .NET 9 and opt-in to the new Lock type via the <EnablePreviewFeatures>true</EnablePreviewFeatures> toggle in your csproj file. That said - there might be breaking changes in the future.

The new Lock type (sitting in System.Threading.Lock) has one main purpose: Being a lock. Yes, you can thank me later for that information. But on a serious note, the new type is a lock and a lock only. There is no ambiguity about what it does. Until now, we have done something like:

private readonly object _lock = new object();

public void DoSomething()
{
    lock (_lock)
    {
        // Do something
    }
}

The new Lock type is a bit more explicit about what it does:

private readonly Lock _lock = new Lock();

public void DoSomething()
{
    lock (_lock)
    {
        // Do something
    }
}

The usage is absolutely the same (currently, more to that later). But the new type is very explicit and, in theory, can be faster. It depends on the underlying implementation that is used. Until now, always a Monitor was involved, but that doesn't have to be from now on (well from November 2024).

But the story doesn't end here necessarily. There is a proposal: "Lock statement pattern ". The idea would be to have a bunch of whole new Lock types. Well, a marker interface like ILockPattern that is "special handled" by the compiler. So given code like this:

class MyDataStructure
{
    private readonly Lock _lock = new();

    void Foo()
    {
        lock (_lock)
        {
            // do something
        }
    }
}

Will be lowered by the compiler as such:

class MyDataStructure
{
    private readonly Lock _lock = new();

    void Foo()
    {
        using (_lock.EnterLockScope())
        {
            // do something
        }
    }
}

Personally, I like reusing the using pattern for such occasions rather than having "multiple" meanings of lock (one for the Monitor and one for the Lock type or whatever underlying type is used). Like Stephen Clearly's AsyncEx library does it:

AsyncLock _mutex = new AsyncLock();
using (await _mutex.LockAsync())
{
    // do something
}

.NET developers are used to the using pattern, and it is a very clear and concise way to express the intention of the code. As this is just a proposal, we will see where we will land.

11
An error has occurred. This application may no longer respond until reloaded. Reload x