Edit

Share via


Lock Class

Definition

Note

To use this preview API, you must enable preview features in your project by setting the EnablePreviewFeatures property to True in your project file. For more information, see https://aka.ms/dotnet-preview-features.

Provides a mechanism for achieving mutual exclusion in regions of code between different threads.

public ref class Lock sealed
[System.Runtime.Versioning.RequiresPreviewFeatures]
public sealed class Lock
[<System.Runtime.Versioning.RequiresPreviewFeatures>]
type Lock = class
Public NotInheritable Class Lock
Inheritance
Lock
Attributes

Remarks

The Lock class can be used to define regions of code that require mutually exclusive access between threads of a process, commonly called critical sections, to prevent concurrent accesses to a resource. A Lock can be entered and exited, where the region of code between the enter and exit is a critical section associated with the lock. A thread that enters a lock is said to hold or own the lock until it exits the lock. At most one thread can hold a lock at any given time. A thread can hold multiple locks. A thread can enter a lock multiple times before exiting it, such as recursively. A thread that can't enter a lock immediately can wait until the lock can be entered or until a specified timeout expires.

When using the Enter or TryEnter methods to enter a lock:

  • Ensure that the thread exits the lock with Exit even in case of exceptions, such as in C# by using a try/finally block.
  • When the lock is being entered and exited in a C# async method, ensure that there is no await between the enter and exit. Locks are held by threads and the code following an await might run on a different thread.

It is recommended to use the EnterScope method with a language construct that automatically disposes the returned Lock.Scope such as the C# using keyword, or to use the C# lock keyword, as these ensure that the lock is exited in exceptional cases. These patterns might also have performance benefits over using Enter/TryEnter and Exit. The following code fragment illustrates various patterns for entering and exiting a lock.

public sealed class ExampleDataStructure
{
    private readonly Lock _lockObj = new();

    public void Modify()
    {
        lock (_lockObj)
        {
            // Critical section associated with _lockObj
        }

        using (_lockObj.EnterScope())
        {
            // Critical section associated with _lockObj
        }

        _lockObj.Enter();
        try
        {
            // Critical section associated with _lockObj
        }
        finally { _lockObj.Exit(); }

        if (_lockObj.TryEnter())
        {
            try
            {
                // Critical section associated with _lockObj
            }
            finally { _lockObj.Exit(); }
        }
    }
}

When using the C# lock keyword or similar to enter and exit a lock, the type of the expression must be precisely System.Threading.Lock. If the type of the expression is anything else, such as Object or a generic type like T, a different implementation that is not interchangeable can be used instead (such as Monitor). For more information, see the relevant compiler speclet.

Interrupt can interrupt threads that are waiting to enter a lock. On Windows STA threads, waits for locks allow message pumping that can run other code on the same thread during a wait. Some features of the waits can be overridden by a custom SynchronizationContext.

Note

A thread that enters a lock, including multiple times such as recursively, must exit the lock the same number of times to fully exit the lock and allow other threads to enter the lock. If a thread exits while holding a Lock, the behavior of the Lock becomes undefined.

Caution

If, on a code path, a thread might enter multiple locks before exiting them, ensure that all code paths that might enter any two of those locks on the same thread enter them in the same order. Otherwise, it could lead to deadlocks. For example, consider that on one code path thread T1 enters lock L1 then lock L2 before exiting both, and on another code path thread T2 enters both locks in the inverse order. In that scenario, it would be possible for the following order of events to occur: T1 enters L1, T2 enters L2, T1 tries to enter L2 and waits, T2 tries to enter L1 and waits. There's a deadlock between T1 and T2 that can't be resolved, and any other threads that try to enter either lock in the future will also hang.

Constructors

Lock()

Initializes a new instance of the Lock class.

Properties

IsHeldByCurrentThread

Gets a value that indicates whether the lock is held by the current thread.

Methods

Enter()

Enters the lock, waiting if necessary until the lock can be entered.

EnterScope()

Enters the lock, waiting if necessary until the lock can be entered.

Equals(Object)

Determines whether the specified object is equal to the current object.

(Inherited from Object)
Exit()

Exits the lock.

GetHashCode()

Serves as the default hash function.

(Inherited from Object)
GetType()

Gets the Type of the current instance.

(Inherited from Object)
MemberwiseClone()

Creates a shallow copy of the current Object.

(Inherited from Object)
ToString()

Returns a string that represents the current object.

(Inherited from Object)
TryEnter()

Tries to enter the lock without waiting.

TryEnter(Int32)

Tries to enter the lock, waiting if necessary for the specified number of milliseconds until the lock can be entered.

TryEnter(TimeSpan)

Tries to enter the lock, waiting if necessary until the lock can be entered or until the specified timeout expires.

Applies to