일반적으로 다중 스레드 프로그래밍이 포함된 한 프로그램의 복잡도는 크게 증가하고, 성능은 크게 떨어지며, 버그 발생 확률도 크게 증가합니다.
멀티스레드 프로그래밍은 데이터 처리 능력을 향상시키기 위해 프로그램을 병렬로 실행하도록 설계되었으나, 대부분의 경우 공유 자원을 두고 경쟁이 발생하므로 자원 객체를 수정할 때는 잠겨 있어야 합니다. 하지만 락을 구현하는 방법은 여러 가지가 있으니, C#에서 여러 종류의 락 구현과 성능을 살펴보겠습니다.
자물쇠를 사용하는 여러 가지 방법
1. 원자 잠금(Atomic lock)
원자 작전 Interlocked를 통해 '락리스' 경쟁을 달성하세요. CompareExchange.
공식 설명은 여러 스레드가 공유하는 변수에 대해 원자 연산을 제공하는 것입니다. 네임스페이스: System.Threading
2. 임계 영역
여러 스레드를 직렬화하여 공용 자원이나 코드 조각에 접근하는 것은 빠르고 데이터 접근 제어에 적합합니다. C#의 락 구문은 중요한 영역(모니터)에 대한 구문 슈가입니다.
3. 원자 연산
원자 연산은 특별한 경우로, 본질적으로 스레드 안전(thread-safe)이기 때문에 잠글 필요가 없습니다.
공식적으로는 특정 변수의 값을 원자 연산 형태로 증가시키고 그 결과를 저장하는 것으로 해석됩니다. 네임스페이스: System.Threading
4. 읽기 및 쓰기 잠금
읽기-쓰기 락은 다른 프로그램이 글을 쓸 때 자원 읽기를 허용하므로, 자원이 더티 리드를 허용한다면 이 방법이 더 적합합니다.
공식 설명에 따르면 자원 접근을 관리하는 데 사용되는 잠긴 상태가 있어 다중 스레드 읽기 또는 독점적 쓰기 접근이 가능하다고 합니다. 네임스페이스는 System.Threading
5. 신호기
제한된 수의 사용자 자원을 제어하도록 설계된 세마포어.
공식 설명은 동시에 자원이나 자원 풀에 접근할 수 있는 스레드 수를 제한합니다. 네임스페이스는 System.Threading
6. 행사
스레드에 일부 이벤트가 발생했다는 알림을 보내며, 후속 작업 시작을 알리는 데 사용됩니다.
공식 설명에 따르면, 스레드 동기화 이벤트는 스레드가 해제된 후 신호가 수신되면 자동으로 초기화된다고 합니다. 이런 유형은 상속될 수 없습니다.
7. 상호 배제
System.스레딩 네임스페이스 바로 아래에 C#에 Mutex 클래스가 있으며, Mutex는 실제로 뮤텍스(mutex)로, 여러 스레드 간의 자원 경쟁뿐만 아니라 프로세스 간의 자원 경쟁도 처리할 수 있습니다.
성능 테스트 코드
코드를 실행하세요
성능 테스트 결과
참고: 위 데이터는 현재 테스트 환경의 하드웨어 성능에 따른 결과일 뿐, 서로 비교할 수 있습니다.
1) 여러 테스트에서 잠기지 않는 것이 확실히 가장 빠르므로, 잠금된 동작으로 이어지는 자원 경쟁을 피하려고 노력하세요.
2) Interlocked.CompareExchange는 멀티스레딩에서 꾸준히 우수한 성능을 보여 2위를 차지합니다.
3) 세 번째 잠금, 즉 치명타 구역도 좋은 성능을 보여주므로, 잠금이 낮다는 말을 다른 사람들이 반박해 주세요.
4) 네 번째는 원자 변수(원자) 연산이지만, 현재는 변수의 자기 증가와 뺄셈만 지원하며 적용 가능성은 강하지 않습니다.
5) 다섯 번째 읽기/쓰기 잠금장치(ReaderWriterLockSlim)의 성능도 괜찮으며, 아무것도 읽지 않고, 실용성도 비교적 좋습니다.
6) 나머지 세마포어, 이벤트, 뮤텍스는 가장 낮은 성능을 보이며, 물론 각자의 적용 범위가 있지만 자원 경쟁을 다루는 데는 좋은 성과를 내지 못합니다.
원본 링크 주소:하이퍼링크 로그인이 보입니다.
|