잠금(컴퓨터 과학)

Lock (computer science)

컴퓨터 과학에서 잠금 또는 뮤텍스(상호 제외로부터)는 동기화 프리미티브로 실행 스레드가 많을 때 리소스에 대한 액세스를 제한하는 메커니즘입니다.잠금은 상호 제외 동시성 제어 정책을 적용하도록 설계되어 있으며, 다양한 방법으로 다양한 응용 프로그램에 대해 여러 고유한 구현이 존재합니다.

종류들

일반적으로 잠금은 각 스레드가 대응하는 데이터에 액세스하기 전에 잠금을 취득함으로써 협력하는 어드바이저리 잠금입니다.일부 시스템에서는 잠긴 리소스에 대한 무단 액세스를 시도하면 액세스를 시도하는 엔티티에서 예외가 강제로 발생하는 필수 잠금 기능도 구현합니다.

가장 간단한 유형의 잠금은 바이너리 세마포어입니다.잠긴 데이터에 대한 배타적 액세스를 제공합니다.다른 방식에서도 데이터를 읽기 위한 공유 액세스를 제공합니다.기타 널리 구현되는 접근모드에는 배타적, 제외 의도 및 업그레이드 의도 등이 있습니다.

잠금을 분류하는 또 다른 방법은 잠금 전략이 스레드의 진행을 방해할 때 발생하는 동작입니다.대부분의 잠금 설계는 잠긴 리소스에 액세스할 수 있을 때까지 잠금을 요청하는 스레드실행을 차단합니다.스핀록을 사용하면 스레드는 잠금을 사용할 수 있을 때까지 대기("스핀")합니다.이는 운영 체제 프로세스 재스케줄링에 따른 오버헤드를 피하기 위해 스레드가 잠시 차단되는 경우에 효율적입니다.장시간 잠금을 유지하는 경우 또는 잠금을 유지하는 스레드의 진행률이 잠긴 스레드의 프리엠프션에 따라 달라지는 경우 비효율적입니다.

일반적으로 잠금을 사용하려면 하드웨어 지원이 필요합니다.이 지원은 보통 "test-and-set", "fetch-and-add" 또는 "compare-and-swap"과 같은 하나 이상의 원자 명령 형식을 취합니다.이러한 지시에 의해, 1개의 프로세스로 잠금이 해방되어 있는지를 테스트하고, 해방되어 있는 경우는, 1개의 원자 조작으로 잠금을 취득할 수 있습니다.

유니프로세서 아키텍처에서는 인터럽트를 일시적으로 디세블로 하기 위해 특별한 명령 또는 명령 프레픽스를 사용하여 무정전 시퀀스를 사용하는 옵션이 있습니다만, 이 기술은 멀티프로세서 공유 메모리머신에서는 동작하지 않습니다.멀티프로세서 환경에서 잠금을 적절하게 지원하려면 상당한 동기화 문제가 있는 복잡한 하드웨어 또는 소프트웨어 지원이 필요할 수 있습니다.

원자 연산이 필요한 이유는 여러 태스크가 동일한 로직을 실행하는 동시성 때문입니다.예를 들어, 다음 C 코드를 생각해 보겠습니다.

한다면 (잠그다 == 0) {     // 잠금 해제, 설정     잠그다 = myPID; } 

위의 예에서는 여러 태스크가 동시에 잠금을 테스트할 수 있으므로 태스크에 잠금이 설정되어 있는 것은 아닙니다.두 작업 모두 잠금이 비어 있음을 감지하므로 두 작업 모두 다른 작업도 잠금이 설정되어 있는지 알지 못하고 잠금을 설정하려고 시도합니다.원자 잠금 작업을 사용할 수 없는 경우 Dekker 또는 Peterson의 알고리즘이 대체될 수 있습니다.

잠금을 부주의하게 사용하면 교착 상태 또는 라이브 잠금을 초래할 수 있습니다.설계 시 및 실행 시 교착 상태 또는 라이브 잠금을 방지하거나 복구하기 위해 여러 가지 전략을 사용할 수 있습니다.(가장 일반적인 전략은 상호의존적인 잠금 조합이 항상 특별히 정의된 "캐스케이드" 순서로 수집되도록 잠금 획득 시퀀스를 표준화하는 것입니다.)

일부 언어에서는 구문적으로 잠금을 지원합니다.C#의 예를 다음에 나타냅니다.

일반의 학급 계좌 // 계정 모니터입니다. {     사적인 십진수 _밸런스 = 0;     사적인 물건 _밸런스록 = 신규 물건();      일반의 무효 보증금(십진수 )     {         // 한 번에 하나의 스레드만 이 문을 실행할 수 있습니다.         잠그다 (_밸런스록)         {             _밸런스 += ;         }     }      일반의 무효 철수하다(십진수 )     {         // 한 번에 하나의 스레드만 이 문을 실행할 수 있습니다.         잠그다 (_밸런스록)         {             _밸런스 -= ;         }     } } 

코드lock(this)인스턴스에 공개적으로 [1]액세스할 수 있는 경우 문제가 발생할 수 있습니다.

Java와 마찬가지로 C#도 MethodImplOptions를 사용하여 메서드 전체를 동기화할 수 있습니다.동기화된 [2][3]어트리뷰트

[MethodImpl(MethodImplOptions)]동기 완료)] 일반의 무효 일부 방법() {     // 일을 하다 } 

입도

잠금 세부사항을 소개하기 전에 잠금에 대한 세 가지 개념을 이해해야 합니다.

  • 잠금 오버헤드: 잠금용으로 할당된 메모리 공간, 잠금을 초기화 및 파기하는 CPU 시간, 잠금을 취득하거나 해제하는 시간 등 잠금을 사용하기 위한 추가 리소스입니다.프로그램이 더 많은 잠금을 사용할수록 사용량에 관련된 오버헤드가 증가합니다.
  • 잠금 경합: 한 프로세스 또는 스레드가 다른 프로세스 또는 스레드에 의해 유지되는 잠금을 획득하려고 시도할 때마다 발생합니다.사용 가능한 잠금이 세밀할수록 한 프로세스/스레드가 다른 프로세스/스레드에 의해 유지되는 잠금을 요구할 가능성이 낮아집니다(예를 들어 테이블 전체가 아닌 행을 잠그거나 전체 행이 아닌 셀을 잠급니다).
  • 데드록: 적어도2개의 태스크가 다른 태스크가 보유하고 있는 잠금을 대기하고 있는 상황.어떤 조치가 취해지지 않으면 두 작업은 영원히 대기하게 됩니다.

동기화 중인 잠금 수를 선택할 때 잠금 오버헤드를 줄이는 것과 잠금 경합을 줄이는 것 사이에는 트레이드오프가 있습니다.

자물쇠의 중요한 특성은 입도입니다.정밀도는 잠금이 보호하고 있는 데이터의 양을 나타내는 척도입니다.일반적으로, 대략적인 정밀도(각각의 록 수가 적고, 데이터의 큰 세그먼트를 보호한다)를 선택하면, 1개의 프로세스가 보호된 데이터에 액세스 하고 있을 때는 오버헤드가 적어지지만, 복수의 프로세스가 동시에 실행되고 있을 때는 퍼포먼스가 저하됩니다.는 잠금 경합이 증가했기 때문입니다.잠금이 거칠수록 잠금이 관련 없는 프로세스의 진행을 멈출 가능성이 높아집니다.반대로 미세하게(더 많은 수의 잠금을 사용하고, 각각 상당히 적은 양의 데이터를 보호함) 사용하면 잠금 자체의 오버헤드는 증가하지만 잠금 경합은 감소합니다.각 프로세스가 공통 잠금 세트의 여러 잠금을 유지해야 하는 세분화된 잠금으로 인해 미묘한 잠금 종속성이 발생할 수 있습니다.이 미묘한 점은 프로그래머가 자신도 모르게 [citation needed]교착 상태를 초래할 가능성을 높일 수 있습니다.

예를 들어 데이터베이스 관리 시스템에서는 입도를 낮추기 위해 필드, 필드, 레코드, 데이터 페이지 또는 테이블 전체를 보호할 수 있습니다.테이블 잠금 사용 등 세세한 부분에서는 단일 사용자에게 최고의 성능을 제공하는 경향이 있지만 레코드 잠금 등의 세밀한 부분에서는 여러 사용자에게 최고의 성능을 제공하는 경향이 있습니다.

데이터베이스 잠금

데이터베이스 잠금은 트랜잭션 동기성을 보장하는 수단으로 사용할 수 있습니다. 즉, 트랜잭션 처리를 동시에(인터리빙 트랜잭션) 할 때, 2단계 잠금을 사용하면 트랜잭션의 동시 실행이 트랜잭션의 일부 직렬 순서와 동등하게 됩니다.그러나 교착상태는 데이터베이스 잠김의 불행한 부작용이 됩니다.교착 상태는 트랜잭션 간의 잠금 순서를 미리 결정함으로써 방지되거나 대기 그래프를 사용하여 탐지됩니다.교착 상태를 방지하면서 데이터베이스 동기화를 위한 잠금 대신 글로벌 타임스탬프를 사용하는 방법이 있습니다.

데이터베이스 상의 여러 동시 사용자의 액션을 관리하기 위해 사용되는 메커니즘이 있습니다.그 목적은 업데이트 손실 및 읽기 불량을 방지하는 것입니다.잠금에는 비관적 잠금과 낙관적 잠금이 있습니다.

  • 비관적인 잠금: 레코드를 업데이트할 목적으로 읽은 사용자는 다른 사용자가 레코드를 조작하지 못하도록 레코드에 배타적 잠금을 설정합니다.즉, 사용자가 잠금을 해제할 때까지 다른 사람은 레코드를 조작할 수 없습니다.단점은 사용자가 매우 오랜 시간 동안 차단될 수 있기 때문에 시스템 전체의 반응이 느려지고 불만을 야기할 수 있다는 것입니다.
비관적인 잠금 사용처: 이는 주로 데이터 컨텐션(사용자가 데이터베이스 시스템에 요구하는 수준)이 높은 환경에서 사용됩니다.동시성 경합이 발생했을 경우 잠금을 통해 데이터를 보호하는 비용이 트랜잭션 롤백 비용보다 적게 듭니다.비관적 동시성은 기록의 프로그래밍 처리에서처럼 잠금 시간이 짧을 때 가장 잘 구현됩니다.비관적인 동시성은 데이터베이스에 대한 지속적인 연결이 필요하며, 레코드가 비교적 오랜 시간 동안 잠겨 있을 수 있기 때문에 사용자가 데이터와 상호 작용할 때 확장 가능한 옵션이 아닙니다.웹 어플리케이션 개발에는 적합하지 않습니다.
  • 최적 잠금: 이를 통해 여러 사용자가 동시에 데이터베이스에 액세스할 수 있으며 각 사용자가 작성한 초기 판독 복사본은 시스템에 보관됩니다.사용자가 레코드를 업데이트하려고 하면 응용 프로그램은 마지막으로 읽은 이후 다른 사용자가 레코드를 변경했는지 여부를 확인합니다.이를 위해 응용 프로그램은 메모리에 저장된 초기 읽기를 데이터베이스 레코드와 비교하여 레코드에 대한 변경 사항을 확인합니다.초기 판독과 데이터베이스 레코드 간의 불일치는 동시성 규칙을 위반하므로 시스템은 업데이트 요청을 무시합니다.오류 메시지가 생성되고 업데이트 프로세스를 다시 시작하라는 메시지가 표시됩니다.필요한 잠금량을 줄여 데이터베이스 서버의 부하를 줄여 데이터베이스 성능을 향상시킵니다.사용자가 잠기지 않기 때문에 제한된 업데이트가 필요한 테이블에서 효율적으로 작동합니다.그러나 일부 업데이트는 실패할 수 있습니다.단점은 여러 사용자의 업데이트 요청이 동시에 대량으로 발생하여 업데이트 실패가 계속된다는 것입니다. 이는 사용자에게 큰 부담이 될 수 있습니다.
최적의 잠금 기능 사용처: 데이터 경합이 적은 환경 또는 데이터에 대한 읽기 전용 액세스가 필요한 환경에 적합합니다.에서는 낙관적인 동시성이 광범위하게 사용되고 있습니다.NET: 장시간 데이터 행을 잠글 수 없는 모바일 [4]및 접속 해제된 애플리케이션의 요구에 대응합니다.또한 레코드 잠금을 유지하려면 데이터베이스 서버에 대한 지속적인 연결이 필요합니다. 이 연결은 연결되지 않은 응용 프로그램에서는 불가능합니다.

단점들

잠금 기반 리소스 보호 및 스레드/프로세스 동기화에는 다음과 같은 많은 단점이 있습니다.

  • 경합: 일부 스레드/프로세스에서는 잠금(또는 전체 잠금 세트)이 해제될 때까지 기다려야 합니다.잠금을 유지하고 있는 스레드 중 하나가 정지, 정지, 차단 또는 무한 루프 상태가 되면 잠금을 기다리는 다른 스레드는 컴퓨터의 전원을 껐다가 켤 때까지 무한히 대기할 수 있습니다.
  • 오버헤드: 잠금을 사용하면 충돌 가능성이 매우 적은 경우에도 리소스에 액세스할 때마다 오버헤드가 증가합니다.(단, 이러한 충돌이 발생할 가능성은 레이스 조건이다.)
  • 디버깅: 잠금과 관련된 버그는 시간에 따라 다르며 매우 미묘하고 데드록과 같은 복제하기가 매우 어려울 수 있습니다.
  • 불안정성: 잠금 오버헤드와 잠금 경합 간의 최적의 균형은 문제 영역(애플리케이션)에 따라 다르며 설계, 구현 및 낮은 수준의 시스템 아키텍처 변경에 민감할 수 있습니다.이러한 균형은 애플리케이션의 라이프 사이클에 따라 변경될 수 있으며 업데이트(재균형화)에 엄청난 변화가 수반될 수 있습니다.
  • 컴포넌트: 잠금 기능은 컴포넌트(예를 들어 테이블A에서 아이템 X를 원자적으로 삭제하고 테이블B에 X를 삽입하기 위해 여러 개의 동시 잠금 관리)만으로 비교적 정교한 소프트웨어 지원 및 엄격한 규약에 대한 애플리케이션 프로그래밍에 의한 완벽한 준거가 가능합니다.
  • priority 반전: priority가 낮은 스레드/프로세스가 공통 잠금을 유지하면 priority가 높은 스레드/프로세스가 진행되지 않도록 할 수 있습니다.priority 상속을 사용하면 priority-inversion 기간을 단축할 수 있습니다.우선도 상한 프로토콜을 단일 프로세서 시스템에서 사용하여 최악의 경우 우선도 반전 기간을 최소화하고 교착 상태를 방지할 수 있습니다.
  • 호송: 다른 모든 스레드는 타임슬라이스 인터럽트 또는 페이지 장애로 인해 잠금을 유지하는 스레드가 해제될 때까지 기다려야 합니다.

일부 동시성 제어 전략은 이러한 문제의 일부 또는 모두를 방지합니다.를 들어, 팬넬 또는 시리얼라이징 토큰은 가장 큰 문제인 교착 상태를 방지할 수 있습니다.잠금 대신 잠금 없는 프로그래밍 기술 및 트랜잭션 메모리와 같은 논블로킹 동기화 방법이 있습니다.그러나 이러한 대체 방법에서는 실제 잠금 메커니즘이 운영 소프트웨어의 보다 근본적인 수준에서 구현되어야 하는 경우가 많습니다.따라서 위의 문제들은 어플리케이션 아래에서 처리해야 할 문제들로 인해 어플리케이션레벨을 잠금 실장의 세부사항에서 해방시킬 수 있습니다.

대부분의 경우 적절한 잠금은 원자 명령 스트림 동기화 방법을 제공하는 CPU에 의존합니다(예를 들어 파이프에 항목을 추가하거나 삭제하려면 파이프 내의 다른 항목을 추가하거나 삭제해야 하는 모든 동시 작업이 추가 또는 추가에 필요한 메모리 내용 조작 중에 중단되어야 합니다).특정 항목을 삭제합니다).따라서 애플리케이션이 운영체제에 부과되는 부담을 인식하고 불가능한 [citation needed]요구의 보고를 정중하게 인식할 수 있을 때 애플리케이션이 더 강력해질 수 있습니다.

구성성 결여

잠금 기반 프로그래밍의 가장 큰 문제 중 하나는 "잠금이 구성되지 않음"입니다. 모듈을 수정하거나 최소한 내부 정보를 파악하지 않고서는 크기가 작고 올바른 잠금 기반 모듈을 동일한 크기의 프로그램으로 결합하는 것이 어렵습니다.Simon Peyton Jones(소프트웨어 트랜잭션 메모리 옹호자)는 뱅킹 애플리케이션의 [5]예를 다음과 같습니다.클래스를 설계여러 동시 클라이언트가 한 계정에 입출금할 수 있는 계정입니다. 또한 한 계정에서 다른 계정으로 송금하는 알고리즘을 제공합니다.문제의 첫 번째 부분에 대한 잠금 기반 솔루션은 다음과 같습니다.

클래스 계정: 멤버 잔액:정수 멤버 뮤텍스: 잠금 메서드 기탁(n: Integer) 뮤텍스.lock() balance ← balance + n mutex.lock() 메서드 인출(n: Integer) 기탁(n)

그 문제의 두 번째 부분은 훨씬 더 복잡하다.시퀀셜 프로그램에 올바른 전송 루틴은 다음과 같습니다.

함수 전송(원:계정, 대상: 계정, 금액: 정수).인출(금액)을 .two.twwwwwwww.

동시 프로그램에서는 이 알고리즘이 잘못되어 있습니다.이는 한 스레드가 이체되는 도중에 다른 스레드가 첫 번째 계정에서 인출되었지만 다른 계정에 아직 입금되지 않은 상태를 확인할 수 있기 때문입니다.즉, 시스템에서 돈이 없어졌습니다.이 문제는 2개의 계정 중 하나를 변경하기 전에 양쪽 계정을 완전히 잠그는 것만으로 해결할 수 있지만 교착 상태를 방지하기 위해 임의의 글로벌 순서에 따라 잠금을 실행해야 합니다.

함수 전송(원:계정, 대상: 계정, 금액: 정수(.lock()에서.lock()까지의 잠금에 대한 임의의 순서일 경우)..to.param()에서 .to.param(to.param)으로 철수(금액)

이 솔루션은 잠금이 많아지면 더욱 복잡해지고 전송 함수는 모든 잠금을 알아야 하므로 잠금이 숨겨져서는 안 됩니다.

언어 지원

프로그래밍 언어는 동기화에 대한 지원이 다양합니다.

  • Ada는 눈에 보이는 보호된 서브 프로그램 또는[6] 엔트리와 랑데부([7]rendezvous)를 가진 보호된 개체를 제공합니다.
  • ISO/IEC C 표준은 C11 이후 표준 상호 제외(잠금) API를 제공합니다.현재의 ISO/IEC C++ 표준은 C++11 이후의 스레드 기능을 지원합니다.OpenMP 표준은 일부 컴파일러에서 지원되며, 플러그마를 사용하여 중요한 섹션을 지정할 수 있습니다.POSIX pthread API는 잠금을 지원합니다.[8]Visual C++는synchronize동기화할 메서드의 속성입니다만, 이것은 Windows 아키텍처 및 Visual C++[9] 컴파일러의 COM 오브젝트에 한정됩니다.C 및 C++는 모든 네이티브 운영 체제 잠금 기능에 쉽게 액세스할 수 있습니다.
  • C#은,lock스레드에 키워드를 지정하여 리소스에 대한 배타적 접근을 보장합니다.
  • VB.NET은,SyncLockC#과 같은 키워드lock키워드를 지정합니다.
  • 자바가 제공하는 키워드는synchronized코드 블록, 메서드 또는[10] 객체 및 동시성 안전 데이터 구조를 특징으로 하는 라이브러리를 잠급니다.
  • Objective-C는 다음 키워드를 제공합니다.@synchronized[11] 코드 블록에 잠금을 설정하고 NSLock,[12] NSRecursiveLock [13]및 NSConditionLock[14] 클래스를 NSLock 프로토콜과[15] 함께 제공합니다.
  • PHP는 파일 기반 잠금과Mutex의 클래스pthreads내선 번호[17]
  • Python은 낮은 수준의 뮤텍스 메커니즘을 제공합니다.Lock에서의 클래스threading모듈로 이동합니다.[18]
  • ISO/IEC Fortran 표준(ISO/IEC 1539-1:2010)은,lock_type고유 모듈의 파생형iso_fortran_env및 그lock/unlockFortran 2008 [19]이후의 스테이트먼트.
  • Ruby는 키워드 [20]없이 낮은 수준의 뮤텍스 개체를 제공합니다.
  • 녹이 슬면Mutex<T>[21] 구조화.[22]
  • x86 어셈블리는LOCK특정 오퍼레이션에 프레픽스를 붙여 원자성을 보증합니다.
  • Haskell은 Haskell이라는 가변 데이터 구조를 통해 잠금을 구현합니다.MVar이 값은 비워 둘 수도 있고 값(일반적으로 리소스에 대한 참조)을 포함할 수도 있습니다.리소스를 사용하려는 스레드가 다음 값을 '취득'합니다.MVar빈 채로 두고, 끝나면 되돌립니다.빈 리소스에서 리소스 가져오기 시도 중MVar그러면 리소스를 사용할 [23]수 있을 때까지 스레드가 차단됩니다.잠금의 대안으로 소프트웨어 트랜잭션 메모리의 구현도 존재한다.[24]
  • Go는 표준 라이브러리 동기화 [25]패키지에 낮은 수준의 Mutex 개체를 제공합니다.코드 블록, 메서드 또는 객체를 잠그는 데 사용할 수 있습니다.

「 」를 참조해 주세요.

레퍼런스

  1. ^ "lock Statement (C# Reference)".
  2. ^ "ThreadPoolPriority, and MethodImplAttribute". MSDN. p. ??. Retrieved 2011-11-22.
  3. ^ "C# From a Java Developer's Perspective". Archived from the original on 2013-01-02. Retrieved 2011-11-22.
  4. ^ "Designing Data Tier Components and Passing Data Through Tiers". Microsoft. August 2002. Archived from the original on 2008-05-08. Retrieved 2008-05-30.
  5. ^ Peyton Jones, Simon (2007). "Beautiful concurrency" (PDF). In Wilson, Greg; Oram, Andy (eds.). Beautiful Code: Leading Programmers Explain How They Think. O'Reilly.
  6. ^ ISO/IEC 8652:2007. "Protected Units and Protected Objects". Ada 2005 Reference Manual. Retrieved 2010-02-27. A protected object provides coordinated access to shared data, through calls on its visible protected operations, which can be protected subprograms or protected entries.
  7. ^ ISO/IEC 8652:2007. "Example of Tasking and Synchronization". Ada 2005 Reference Manual. Retrieved 2010-02-27.
  8. ^ Marshall, Dave (March 1999). "Mutual Exclusion Locks". Retrieved 2008-05-30.
  9. ^ "Synchronize". msdn.microsoft.com. Retrieved 2008-05-30.
  10. ^ "Synchronization". Sun Microsystems. Retrieved 2008-05-30.
  11. ^ "Apple Threading Reference". Apple, inc. Retrieved 2009-10-17.
  12. ^ "NSLock Reference". Apple, inc. Retrieved 2009-10-17.
  13. ^ "NSRecursiveLock Reference". Apple, inc. Retrieved 2009-10-17.
  14. ^ "NSConditionLock Reference". Apple, inc. Retrieved 2009-10-17.
  15. ^ "NSLocking Protocol Reference". Apple, inc. Retrieved 2009-10-17.
  16. ^ "flock".
  17. ^ "The Mutex class".
  18. ^ Lundh, Fredrik (July 2007). "Thread Synchronization Mechanisms in Python". Archived from the original on 2020-11-01. Retrieved 2008-05-30.
  19. ^ John Reid (2010). "Coarrays in the next Fortran Standard" (PDF). Retrieved 2020-02-17.
  20. ^ "Programming Ruby: Threads and Processes". 2001. Retrieved 2008-05-30.
  21. ^ "std::sync::Mutex - Rust". doc.rust-lang.org. Retrieved 3 November 2020.
  22. ^ "Shared-State Concurrency - The Rust Programming Language". doc.rust-lang.org. Retrieved 3 November 2020.
  23. ^ Marlow, Simon (August 2013). "Basic concurrency: threads and MVars". Parallel and Concurrent Programming in Haskell. O’Reilly Media. ISBN 9781449335946.
  24. ^ Marlow, Simon (August 2013). "Software transactional memory". Parallel and Concurrent Programming in Haskell. O’Reilly Media. ISBN 9781449335946.
  25. ^ "sync package - sync - pkg.go.dev". pkg.go.dev. Retrieved 2021-11-23.

외부 링크