본문 바로가기
Program/C# .NET

[C# WPF] MultiThread 환경에서 로그 작성하기 / Log 누락 방지하기 / ReaderWriterLock과 ReaderWriterLockSlim 차이점

by 냠만 2025. 4. 2.

[ReaderWriterLock 개요]

개발을 진행하다 보면 좋든 싫든 Thread를 사용하게 됩니다. 모든 작업에 병렬처리는 기본적으로 탑재되어 있는 기능이라 보는 수준이 되었기 때문입니다. 현재 PC의 하드웨어 사양도 그에 맞게 멀티스레드 멀티코어를 지양합니다. 유치원 문제를 해결하는 데 있어서 고등학생 1명과 중학생 10명에게 문제를 배분하고 해결하는 데 걸리는 시간을 측정하는 것과 같지요. 

이에 따라 Task나 Thread 등을 사용하여 동시에 업무를 처리하곤 하는데 이에 따라 발생되는 문제점을 일일이 분석하기에는 어려움이 있습니다. 그래서 한 개의 문서에 모든 일꾼의 업무를 시간순으로 나열하고 싶을 때 사용되는 방법입니다. 일반적으로 다양한 일꾼들에게서 나오는 시스템메시지를 각각의 Thread 각각 다른 File에 업무내용을 작성하는 건 쉽습니다. 각각 일꾼들에게 다른 종이와 볼펜을 나눠주면 간단하기 때문이죠.

그러나 한 개의 종이와 한 개의 볼펜으로 모든 일꾼들에게 일을 할 때마다 와서 체크를 하라고 지시하면 와서 줄 서있는 시간만 잔뜩 잡아먹고 비효율적이게 됩니다.

심지어 어떤 일꾼은 자신의 업무를 작성하러 왔다가 볼펜이 이미 사용중임을 깨닫고 작성을 안 해버리는 경우도 더러 있습니다.

하면 모든 일꾼들이 어떤순서로 일을 했고 어떤 효율성을 가지고 있는지 파악하기 어렵습니다.

이럴 때 ReaderWriterLock을 사용하게 됩니다.

 

MSDN에 명시된 해당 함수의 정의는 아래와 같습니다.

ReadWriterLock : 리소스를 동기화하는 데 사용합니다. 지정된 시간에 여러 스레드에 대한 동시 읽기 액세스 또는 단일 스레드에 대한 쓰기 액세스를 허용합니다. 판독기와 작성기는 별도의 큐에 대기합니다.

ReaderWriterLockSlim : ReadWriterLock보다 성능이 좋으나 .NET5 이상에서 권장합니다.

 

ReaderWriterLockSlim은 데이터를 쓰기 위해 접근할 때는 Lock을 설정하고 읽기 모드일 경우 Lock을 설정하지 않아 동시 접근성을 확보해 주는 아주 친절한 친구입니다. 중요한 점은 slim이 일반 lock보다 리소스를 적게 사용하며 큐에 대기한다는 점입니다. 따라서 데이터 누락이나 중복을 제어할 수 있는 중요한 기능이 내재되어 있습니다.

 

[ReaderWriterLockSlim 사용법]

먼저 Thread를 두 개 구현합니다.

Thread 1 : 1 ~ 10까지 기록합니다.

Thread 2 : 1 ~ 10까지 기록합니다.

 

ReaderWriterLock 방식과 ReaderWriterLockSlim 방식 두 가지를 사용하여 같은 파일에 두 개의 스레드에서 로그를 작성합니다.

일반 버튼 클릭하면 Thread 두 개가 선언되고 실행되어 로그를 작성하고 종료되는 간단한 구조입니다.

로그를 작성하는 방법은 위에서 소개한 두 개 방식을 모두 사용하며 각각 결과값은 txt파일로 작성됩니다.

 

작성되는 경로는 해당 프로젝트 디폴트 폴더의 내부 LOG폴더로 지정하였고 폴더나 기존 파일이 없는 경우 1회 작성 후 루프를 돌며 로그를 작성합니다.

작성 도중 파일에 대한 접근에 대해서는 EnterWriteLock을 사용하고 작성하고자 하는 로그를 큐에 등록시킨 후 Lock을 빠져나옵니다.

 

결과입니다. 일반적으로 ReaderWriterLock방식을 사용했을 경우 ReaderWriterLockSlim 방식보다 시간이 더 오래 소요되었으며 스레드에 대한 로그도 중복처리 된 케이스가 존재하여 안전성이 떨어진다고 판단할 수 있습니다.

내부 원인에 대한 부분은 추가 분석이 필요하겠지만 단순한 테스트에서도 성능이 검증되는 만큼 사용에 유의하시면 되겠습니다.

 

[결론]

1. ReaderWriterLock 보다 ReaderWriterLockSlim 사용하자.

2. ReaderWriterLockSlim이어도 무분별한 사용은 쓸모없는 임계영역 진입으로 오버헤드를 발생시킨다.

3. 단일 스레드는 object lock을 활용하자.

4. 멀티 스레드에서 공유되어야 하는 자원이 한정적인 경우 (특정 변수 한 개에서 두 개) Interlocked을 활용하는 게 좋다.

 

추가로 Interlocked를 활용하는 방법은 다음 글에 포스팅하도록 하겠습니다.