낑깡의 게임 프로그래밍 도전기

Unity C# : Lock (잠금) 메커니즘 / 동기화 메커니즘 본문

Unity C#

Unity C# : Lock (잠금) 메커니즘 / 동기화 메커니즘

낑깡겜플밍 2024. 5. 30. 17:22
반응형

두개의 박스가있는데 둘 다 첫번째 박스에 들어가는데 첫번째 박스가 차있으면 두번째 박스로 가게끔해야하는 상황.

만약 호출시기가 똑같으면 보통 알아서 각자 들어가는게 아니라 에러가 발생한다.

 

이럴 때 Lock 매커니즘을 적절히 사용하면 두 개뿐만 아니라 10개, 그 이상이 동시에 실행되는 상황에서도 안전하게 처리할 수 있다. 중요한 것은 동기화 블록을 통해 동일한 코드 영역에 대한 접근을 일관되게 제어하는 것.

 

동기화 메커니즘 사용

요약

lock(object T)
{
 원하는 코드;
}
  • Lock (잠금) 메커니즘: C#의 lock 문을 사용하여 첫 번째 박스를 확인하고 아이템을 넣는 부분을 잠그면, 다른 스크립트가 이 부분을 동시에 실행하지 않도록 할 수 있음.(특정 객체를 기준으로 다른 스레드가 동일한 코드 블록에 접근X)
private static readonly object _lock = new object();

void AddItemToBox(Item item) 
{
    lock (_lock) 
    {
        if (IsFirstBoxEmpty()) 
            AddToFirstBox(item);
            
        else 
            AddToSecondBox(item);
     }
}

작동 방식 설명

  1. 동기화 블록 설정: lock (_lock) 구문을 통해 _lock 객체를 기준으로 잠금 설정.
  2. 잠금 획득: 한 스레드가 lock 블록을 실행하면, _lock 객체에 대한 잠금을 획득하고 나서야 블록 내부의 코드를 실행.
  3. 다른 스레드 대기: 잠금을 획득한 스레드가 블록 내부의 코드를 실행하는 동안, 다른 스레드는 잠금이 해제될 때까지 대기.
  4. 잠금 해제: 블록 내부의 코드가 모두 실행되면, 잠금이 해제되고 다른 대기 중인 스레드가 잠금을 획득하여 블록 내부의 코드를 실행.

동시 실행 시나리오

이 메커니즘을 사용하면 다음과 같은 방식으로 동작합니다:

  • 두 개의 스레드가 동시에 AddItemToBox 함수를 호출하려고 할 때, 먼저 호출한 스레드가 _lock 객체에 대한 잠금을 획득.
  • 이 스레드는 블록 내부의 코드를 실행한 후 잠금을 해제.
  • 두 번째 스레드는 첫 번째 스레드가 잠금을 해제한 후에야 블록 내부의 코드를 실행할 수 있음.

 

반응형

---------------------------------------------------------------------------------------------------------------------------------------------------------------

상세 추가 설명과 데드락 관련 내용 추가 : 아래 블로그 참조

 

lock()의 파라미터에는 임의의 객체를 사용할 수 있는데, 주로 object 타입의 private 필드를 지정한다.

즉, private object obj = new object() 와 같이 private 필드를 생성한 후, lock(obj)와 같이 지정한다.

 

특히, 클래스 객체를 의미하는 this를 사용하여 lock(this)와 같이 잘못 사용할 수 있는데, 이는 의도치 않게 데드락을 발생시키거나 Lock Granularity를 떨어뜨리는 부작용을 야기할 수 있다. 즉, 불필요하게 객체 전체 범위에서 lock을 걸어 다수의 메서드가 공유 리소스를 접근하는데 Lock Granularity를 떨어뜨리는 효과가 있으며 (주: 이 경우 필드나 속성들이 잠긴다는 의미는 아니며, 이들 필드나 속성을 계속 읽고 쓸 수 있음) , 또한 만약 외부에서 해당 클래스 객체를 lock 하고 그 객체 안의 메서드를 호출한 경우 그리고 그 메서드안에서 다시 lock(this)를 하는 경우, 이미 외부에서 잡혀있는 lock이 풀리기를 메서드 내에서 계속 기다릴 것이므로 데드락이 발생할 수 있다.

그리고 Critical Section 코드 블록은 가능한 한 범위를 작게하는 것이 좋은데, 이는 필요한 부분만 Locking한다는 원칙에 따른 것이다.

using System;
using System.Threading;

namespace MultiThrdApp
{
    class MyClass
    {
        private int counter = 1000;

        // lock문에 사용될 객체
        private object lockObject = new object();

        public void Run()
        {
            // 10개의 쓰레드가 동일 메서드 실행
            for (int i = 0; i < 10; i++)
            {
                new Thread(SafeCalc).Start();    
            }
        }

        // Thread-Safe 메서드 
        private void SafeCalc()
        {
            // 한번에 한 쓰레드만 lock블럭 실행
            lock (lockObject)
            {
                // 필드값 변경
                counter++;

                // 가정 : 다른 복잡한 일을 한다
                for (int i = 0; i < counter; i++)
                    for (int j = 0; j < counter; j++) ;

                // 필드값 읽기
                Console.WriteLine(counter);
            }
        }

        //출력 예:
        // 1001
        // 1002
        // 1003
        // 1004
        // 1005
        // 1006
        // 1007
        // 1008
        // 1009
        // 1010
    }
}

https://www.csharpstudy.com/Threads/lock.aspx

 

C# lock 블럭 - C# 프로그래밍 배우기 (Learn C# Programming)

Thread-Unsafe의 예제 멀티쓰레드는 필드의 내용을 동시에 엑세스하여 잘못된 결과를 만들거나 출력할 수 있는데, 이를 Thread Unsafe 하다고 한다. 아래 예제는 여러 개의 쓰레드가 Thread-Safe하지 않은

www.csharpstudy.com

 

반응형