C #에서는 스레드 안전을 위해 Queue.Synchronized 또는 lock ()을 사용하는 것이 더 낫습니까?
스레드로부터 안전한지 확인해야하는 Queue 개체가 있습니다. 다음과 같은 잠금 객체를 사용하는 것이 더 낫습니까?
lock(myLockObject)
{
//do stuff with the queue
}
또는 다음과 같이 Queue.Synchronized를 사용하는 것이 좋습니다.
Queue.Synchronized(myQueue).whatever_i_want_to_do();
MSDN 문서를 읽음으로써 스레드로부터 안전하도록 Queue.Synchronized를 사용해야한다고 말했지만 잠금 개체를 사용하는 예제를 제공합니다. MSDN 기사에서 :
큐의 스레드 안전성을 보장하려면 모든 작업이이 래퍼를 통해서만 수행되어야합니다.
컬렉션을 통한 열거는 본질적으로 스레드로부터 안전한 프로 시저가 아닙니다. 컬렉션이 동기화 된 경우에도 다른 스레드가 컬렉션을 수정할 수 있으므로 열거자가 예외를 throw합니다. 열거하는 동안 스레드 안전을 보장하기 위해 전체 열거 중에 컬렉션을 잠 그거나 다른 스레드의 변경으로 인한 예외를 포착 할 수 있습니다.
Synchronized ()를 호출해도 스레드 안전성이 보장되지 않는다면 그 요점은 무엇입니까? 여기에 뭔가 빠졌나요?
개인적으로 저는 항상 잠금을 선호합니다. 그것은 당신 이 세분성을 결정할 수 있다는 것을 의미합니다 . 동기화 된 래퍼에만 의존하는 경우 각 개별 작업이 동기화되지만 한 가지 이상을 수행해야하는 경우 (예 : 전체 컬렉션에 대한 반복) 어쨌든 잠글 필요가 있습니다. 단순함을 위해 기억해야 할 한 가지만있는 것이 좋습니다. 적절하게 잠그십시오!
편집 : 주석에서 언급했듯이 더 높은 수준의 추상화를 사용할 수 있다면 좋습니다. 당신이 있다면 어떻게 사용 잠금을, 그것으로 조심 - 당신이 무엇을 기대 문서는 곳 잠김 (자세한 성능보다 정확성을 위해) 가능한 한 짧은 기간 동안 획득 / 해제 잠금 할 수 있습니다. 잠금을 유지하는 동안 알 수없는 코드를 호출하지 말고 중첩 된 잠금 등을 피하십시오.
.NET 4에는 더 높은 수준의 추상화 (잠금없는 코드 포함) 가 훨씬 더 많이 지원됩니다. 어느 쪽이든 동기화 된 래퍼를 사용하지 않는 것이 좋습니다.
Synchronized
이전 컬렉션 라이브러리 의 메서드에는 너무 낮은 수준의 세분화 (작업 단위가 아닌 메서드 별)로 동기화된다는 점에서 큰 문제가 있습니다.
대기열에서 Count
빼는 것이 안전한지 확인하기 위해 를 확인하는 아래에 표시된 동기화 된 대기열이있는 고전적인 경쟁 조건이 있지만이 Dequeue
메서드는 대기열이 비어 있음을 나타내는 예외를 throw합니다. 이는 각 개별 작업이 스레드로부터 안전하기 때문에 발생하지만의 값은 Count
쿼리 할 때와 값을 사용할 때 사이에 변경 될 수 있습니다.
object item;
if (queue.Count > 0)
{
// at this point another thread dequeues the last item, and then
// the next line will throw an InvalidOperationException...
item = queue.Dequeue();
}
다음과 같이 전체 작업 단위 (예 : 개수 확인 및 항목 대기열에서 빼기)에 대한 수동 잠금을 사용하여 안전하게 작성할 수 있습니다 .
object item;
lock (queue)
{
if (queue.Count > 0)
{
item = queue.Dequeue();
}
}
따라서 동기화 된 대기열에서 안전하게 대기열을 뺄 수 없으므로 신경 쓰지 않고 수동 잠금 만 사용합니다.
.NET 4.0에는 적절하게 구현 된 스레드로부터 안전한 컬렉션이 많이 있어야하지만 안타깝게도 아직 1 년 정도 남았습니다.
'스레드 안전 컬렉션'에 대한 요구와 원자 적 방식으로 컬렉션에 대해 여러 작업을 수행해야하는 요구 사항 사이에는 종종 긴장이 있습니다.
따라서 Synchronized ()는 여러 스레드가 동시에 항목을 추가하는 경우 자체적으로 부숴지지 않는 컬렉션을 제공하지만 열거 중에 다른 사람이 만져서는 안된다는 것을 아는 컬렉션을 마술처럼 제공하지는 않습니다.
열거뿐만 아니라 "이 항목이 이미 대기열에 있습니까? 아니요, 추가하겠습니다"와 같은 일반적인 작업에는 대기열보다 더 넓은 동기화가 필요합니다.
이렇게하면 큐가 비어 있는지 확인하기 위해 큐를 잠글 필요가 없습니다.
object item;
if (queue.Count > 0)
{
lock (queue)
{
if (queue.Count > 0)
{
item = queue.Dequeue();
}
}
}
lock (...) {...} 잠금을 사용하는 것이 정답이라는 것이 분명해 보입니다.
큐의 스레드 안전성을 보장하려면 모든 작업이이 래퍼를 통해서만 수행되어야합니다.
다른 스레드가 .Synchronized ()를 사용하지 않고 큐에 액세스하면 모든 큐 액세스가 잠기지 않는 한 크릭이 될 것입니다.
'Programing' 카테고리의 다른 글
정적 변수 초기화 순서 (0) | 2020.12.12 |
---|---|
Linux에서 로컬 함수 호출을 추적하는 도구 (0) | 2020.12.12 |
실제로 C ++에서 Private 또는 Protected 상속이 필요한 이유는 무엇입니까? (0) | 2020.12.12 |
.RData 파일에서 데이터를 보는 방법은 무엇입니까? (0) | 2020.12.12 |
마우스로 Three.js에서 카메라 회전 (0) | 2020.12.12 |