💡 C# 비동기 코드에서 락(lock) 쓰는 법 정리

 




C#에서 async/await를 사용할 때 lock을 함께 쓰려고 하면 에러가 난다. 

이때 어떻게 하면 lock을 쓸 수 있을까? 🤯

 


 

🔐  전통적인 lock과 비동기 코드의 문제점

 

lock문은 동기(synchronous)코드에서만 사용 가능하다.

비동기 코드에서 await를 만나면 스레드가 컨텍스트를 해제하면서 락을 제대로 해제하지 못하는 상황이 생길 수 있기 때문이다.

private static readonly object _lock = new object();

public async Task DoSomethingAsync()
{
    lock (_lock) // 비동기 코드에서는 사용 불가!
    {
        await Task.Delay(1000);
    }
}

 

결과? 👉 에러! The 'await' operator cannot be used in a 'lock' statement. 😭

 

 


 

🚦 해결 방법: SemaphoreSlim 사용하기!

비동기 환경에서는 ** SemaphoreSlim **을 쓰면 깔끔하게 해결 가능하다. 
전통적인 락 대신 WaitAsync()와 Release()로 락을 관리하는 방식이다. 

 

private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

public async Task DoSomethingAsync()
{
    await _semaphore.WaitAsync(); // 락 획득
    try
    {
        Console.WriteLine("작업 시작!");
        await Task.Delay(1000); // 어떤 비동기 작업
        Console.WriteLine("작업 완료!");
    }
    finally
    {
        _semaphore.Release(); // 락 해제
    }
}

 

이렇게 하면 데드락 없이 안전하게 비동기 코드에서도 락을 걸 수 있다. 

 


 

🛠️ 더 깔끔한 방법: AsyncLock 클래스 만들기!

 

여러 곳에서 락을 쓰려면, SemaphoreSlim을 직접 쓰는 것보다
AsyncLock 클래스를 만들어서 관리하는 게 더 편리하다.

 

 

AsyncLock

public sealed class AsyncLock
{
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

    public async Task<IDisposable> LockAsync()
    {
        await _semaphore.WaitAsync();
        return new Releaser(_semaphore);
    }

    private sealed class Releaser : IDisposable
    {
        private readonly SemaphoreSlim _semaphore;

        public Releaser(SemaphoreSlim semaphore)
        {
            _semaphore = semaphore;
        }

        public void Dispose()
        {
            _semaphore.Release();
        }
    }
}

 

 사용 예제 

 

private static readonly AsyncLock _lock = new AsyncLock();

public async Task DoSomethingAsync()
{
    using (await _lock.LockAsync()) // 락 자동 관리
    {
        Console.WriteLine("임계 영역 진입!");
        await Task.Delay(1000);
        Console.WriteLine("임계 영역 종료!");
    }
}

 

 


 

요약 정리

  1. 전통적 lock → 비동기에서 사용 ❌
  2. SemaphoreSlim → 비동기 락 가능! ✅
  3. AsyncLock → 재사용성 높고 깔끔하게 락 관리! ✨

 

 

 

 


 

[참고]

 

https://jacking75.github.io/csharp_asyncawait_lock/ 

 

C# - async-await에서 lock 사용하기 - jacking75

출처 C#에서 비동기 메소드에서는 lock을 쓸 수 없다. 이 글은 그래도 lock을 사용 하고 싶을 때를 위한 것이다. lock이 필요한 경우를 예를 들면 아래처럼 더블 체크 락킹을 하고 싶을 때이다. // /이

jacking75.github.io


https://medium.com/@tyschenk20/mixing-traditional-locks-with-async-code-in-c-27431f857e01