2010-05-05 6 views
2

동시 시스템에서 동일한 값에 대한 읽기 및 쓰기 액세스가 필요한 현실적인 예를 찾고 있습니다.Real World 동시 소프트웨어에서 읽기/쓰기의 예

제 생각에는 구현 자에게 알려진 대안이 없기 때문에 많은 세마포 또는 잠금이 있지만 뮤텍스가 요구 사항 인 것처럼 보이는 패턴을 알고 있습니까?

현실 세계에서 동시 소프트웨어의 표준적인 하드 문제에 대한 후보자를 묻는 중입니다.

답변

5

어떤 종류의 잠금이 사용되는지는 여러 스레드가 데이터에 액세스하는 방법에 따라 다릅니다. 유스 케이스를 미세하게 조정할 수있는 경우 가끔 독점 잠금의 필요성을 완전히 제거 할 수 있습니다.

독점 잠금은 공유 데이터가 항상 100 % 정확해야한다는 유스 케이스가 필요한 경우에만 필요합니다. 이것은 대부분의 개발자들이 시작하는 기본값입니다. 그 이유는 이것이 우리가 데이터에 대해 생각하는 방식이기 때문입니다.

그러나 데이터를 사용하는 경우 "느슨 함"을 허용 할 수있는 경우 모든 액세스에서 독점적 인 잠금을 사용하지 않고 스레드간에 데이터를 공유하는 몇 가지 기술이 있습니다.

예를 들어 링크 된 데이터 목록이 있고 해당 링크 목록을 사용하면 목록 통과에서 동일한 노드를 여러 번 보아도 두려워하지 않고 삽입 링크가 보이지 않으면 당황하지 않을 것입니다 삽입 (또는 유사한 아티팩트) 직후 삽입 또는 삭제 작업에 대해 완전 중지 뮤텍스 잠금을 사용하지 않고 원자 포인터 교환을 사용하여 목록 삽입 및 삭제를 수행 할 수 있습니다.

또 다른 예 : 대부분 스레드에서 읽고 마스터 스레드에 의해서만 때때로 업데이트되는 배열 또는 목록 개체가있는 경우 목록의 두 복사본을 유지 관리하여 잠금없는 업데이트를 구현할 수 있습니다. 하나는 " 다른 스레드가 읽을 수있는 "라이브"와 자신의 스레드의 개인 정보 보호에서 쓸 수있는 "오프라인"이 있습니다. 업데이트를 수행하려면 "라이브"목록의 내용을 "오프라인"목록에 복사하고 오프라인 목록을 업데이트 한 다음 원자 포인터 교환을 사용하여 오프라인 목록 포인터를 라이브 목록 포인터로 바꿉니다. 이제 독자는 이제 오프라인 목록에서 "유출"할 수있는 메커니즘이 필요합니다. 가비지 수집 시스템에서 오프라인 목록에 대한 참조를 해제 할 수 있습니다. 마지막 소비자가 완료되면 GC'd가됩니다. 비 GC 시스템에서는 참조 카운팅을 사용하여 얼마나 많은 독자가 여전히 목록을 사용하고 있는지 추적 할 수 있습니다. 이 예제의 경우 목록 업데이터로 지정된 스레드가 하나만있는 것이 이상적입니다. 복수의 updater가 필요한 경우는, 갱신 조작에 락을 걸 필요가 있습니다 만, 갱신 원을 직렬화하는 것만으로, 락의 영향을받지 않고리스트의 독자에게 퍼포먼스에 영향을주지 않습니다.제가 알고

모든 잠금이없는 자원 공유 기술은 원자 스왑 (일명 InterlockedExchange)의 사용을 필요로한다. 이것은 대개 CPU 및/또는 하드웨어 버스 잠금 (x86 어셈블러에서 읽기 또는 쓰기 opcode의 잠금 접두사)의 특정 명령으로 변환됩니다. multiproc 시스템에서 원자 스왑은 다른 프로세서에서 캐시 무효화를 강제 할 수 있습니다 (이는 이중 proc Pentium II의 경우였습니다). 그러나 이것이 현재의 멀티 코어 칩에서 많은 문제는 아니라고 생각합니다. 이러한 성능 경고가 있더라도 잠금 해제는 전체 중지 커널 이벤트 객체를 사용하는 것보다 훨씬 빠르게 실행됩니다. 커널 API 함수를 호출하기 만하면 수백 클럭 사이클이 걸립니다 (커널 모드로 전환). 실제 시나리오의

예 :

  1. 생산자/소비자 워크 플로우. 웹 서비스는 데이터에 대한 http 요청을 수신하고 요청을 내부 대기열에 저장하며 작업자 스레드는 작업 항목을 대기열에서 가져 와서 작업을 수행합니다. 큐는 읽기/쓰기이며 스레드로부터 안전해야합니다.
  2. 소유권 변경으로 스레드간에 공유되는 데이터. 스레드 1은 객체를 할당하고 처리를 위해 스레드 2에 던져 놓고 다시는보고 싶지 않습니다. 쓰레드 2는 객체를 처리합니다. 메모리 관리 시스템 (malloc/free)은 스레드로부터 안전해야합니다.
  3. 파일 시스템. 이것은 거의 항상 OS 서비스이며 이미 스레드로부터 안전합니다. 그러나 목록에 포함하는 것이 좋습니다.
  4. 참조 카운팅. 참조 수가 0이되면 리소스를 해제합니다. 증가/감소/테스트 작업은 스레드로부터 안전해야합니다. 이것들은 대개 full-stop kernal 뮤텍스 잠금 대신 원자 기본을 사용하여 구현할 수 있습니다. 당신이이 잠금의 마지막을 적용하려면 어 그리 게이터로가는 여러 개의 큐를 할 수없는 이유 합리적인 소리
2

대부분의 실제 세계의 동시 소프트웨어는 일정 수준의 동기화 요구 사항이 있습니다. 종종 더 잘 작성된 소프트웨어는 필요한 잠금 량을 줄이기 위해 큰 노력을 기울이지 만 어느 시점에서는 여전히 필요합니다.

예를 들어, 우리는 어떤 형태의 집계 연산이 발생하는 곳에서 종종 시뮬레이션을 수행합니다. 일반적으로 시뮬레이션 단계 자체에서 잠금을 방지하는 방법 (예 : 스레드 로컬 상태 데이터 사용 등)이 있지만 실제 집계 부분에는 일반적으로 끝 부분에 잠금이 필요합니다.

다행히도 이것은 작업 단위당이 아니라 스레드 당 잠금이됩니다. 필자가 일반적으로 수십만 또는 수백만 개의 작업 단위로 작업을 수행하고 있기 때문에 필자의 경우 이것은 중요합니다. 그러나 대개의 경우 PE가 4-16 개있는 시스템에서 발생합니다. 이는 일반적으로 비슷한 수의 실행 단위. 이러한 유형의 메커니즘을 사용하면 여전히 잠금 상태이지만 잠재적으로 수백만 개가 아닌 수십 개의 요소 사이에서 잠글 수 있습니다.

+0

, 내가 부탁 할 수 있습니까? –

+0

@ 리차드 : 잠재적으로 수 - 공유하지만, 잠금없는 큐에 펌핑 데이터의 오버 헤드는 다음 직렬 나중에 동기화하는 데 필요한 환원 로킹보다 높은 큐 처리. –

+0

@ 리차드 : 잠금 장치가 적절하게 사용된다면 잠금 자체가 반드시 나쁜 것은 아닙니다. 잠김이 대안보다 더 나은 선택 일 때가 있지만, 프로파일 링을 통해서만 확신 할 수 있습니다. –