.NET Client Documentation
5.3.0
Search Results for

    Show / Hide Table of Contents

    Locking

    On the server (member) side, Hazelcast uses a unique number to identify the owner of locks, and historically that number has always been the thread unique identifier. As a consequence, the locking model in previous versions of the Hazelcast client closely match the thread-based model that .NET provides with, for instance, the lock statement.

    Due to the systematic usage of asynchronous patterns, the code for one operation can be executed by many different threads (basically, each time an operation is put on hold by an await statement, it can resume its execution on any other thread). Therefore, using the actual thread identifier as a "lock owner" identifier is not possible anymore.

    The Hazelcast .NET client introduces different ways to manage the context of a lock in an asynchronous pattern.

    Implicit Context

    For things such as maps, that would implicitly rely on the thread identifier to identify their lock context, the Hazelcast .NET client implicitly manages a "lock context". This allows the API to remain similar enough to the version 3 API. The lock context is represented by an AsyncContext instance. This is a class which relies upon the .NET built-in AsyncLocal<T> type to maintain values that flow with the asynchronous operation, i.e. are transferred to the new thread when an operation resumes after awaiting. Therefore, when an operation acquires a lock, it owns the lock until it releases it, no matter what thread executes the operation. The AsyncContext uses a sequential number to ensure the uniqueness of the identifier.

    In order to execute work in a new context (which would correspond to executing work on a different thread for previous versions), one has to use a new context:

    // executes in the same, current context
    await DoSomethingAsync(...);
    
    using (AsyncContext.New())
    {
        // executes in a new context
        await DoSomethingAsync(...);
    }
    

    Due to the way AsyncLocal<T> variables work, any task started from within the using block executes in the new context, even if it continues to execute after the using block has exited:

    Task task;
    
    using (AsyncContext.New())
    {
        // starts in a new context
        var task = DoSomethingAsync(...);
    }
    
    // the entire task executes in the new context
    await task;
    

    Essentially, when the using block is exited, the previous AsyncContext is restored, but the new one that was created remains attached to the tasks that were started.

    Explicit Context

    On the other hand, fenced locks, which are part of the CP subsystem, rely on an explicit object to represent the lock context. Every fenced lock operation requires a context, and operate on that context. Therefore, acquiring a lock is performed as:

    var context = new LockContext();
    var lock = await client.CPSubSystem.GetLockAsync(lockName);
    await lock.LockAsync(context);
    // ...
    await lock.UnlockAsync(context);
    

    The lock context is re-entrant: it is possible to lock several times for the same context as long as the lock is unlocked the same number of times. On the other hand, trying to lock for any other context object would block.

    It is up to the application code to manage the lock context appropriately.

    In This Article
    Back to top Copyright © 2010-2023 Hazelcast, Inc. All rights reserved.
    Generated by DocFX.