2009-02-04 2 views
8

모든 스레드에서 읽고 쓰는 데 '부울'변수가 스레드로부터 안전합니까? 나는 그것이 있다고 말하는 어떤 뉴스 그룹 참조를 보았다. 다른 데이터 유형이 있습니까? (열거 형, 짧은 int 아마도?)'thread-safe'읽기/쓰기 연산을 사용하는 Delphi 데이터 유형 목록?

어떤 스레드에서 안전하게 읽을 수있는 모든 데이터 형식의 목록을 가지고 있으면 좋을 것입니다. 그리고 어떤 스레드에서도 안전하게 쓸 수있는 또 다른 목록은 리조트에 가지 않아도됩니다. 다양한 동기화 방법.

답변

7

delphi에서 모든 것을 안전하고 안전하게 처리 할 수 ​​있습니다. 다른 사람들은 부울에 정렬 문제를 언급하는 반면에 이것은 실제 문제를 숨 깁니다.

예. 모든 스레드에서 부울을 읽을 수 있으며 올바르게 정렬 된 경우 모든 스레드에서 부울에 쓸 수 있습니다. 하지만 변경 한 부울에서 읽는 것이 어쨌든 "스레드 안전"일 필요는 없습니다. 다른 스레드가 숫자를 읽도록 숫자를 업데이트 한 경우 true로 설정된 부울 값이 있다고합시다.

if NumberUpdated then 
begin 
    LocalNumber = TheNumber; 
end; 

때문에 최적화에 프로세서 따라서 당신은 당신이 마지막으로 NumberUpdated 업데이트에 있는데도 TheNumber의 이전 값을 얻을 수 NumberUpdated 읽기 전에 TheNumber를 읽을 수 있습니다.

아카, 당신의 코드가 될 수 있습니다 :

temp = TheNumber; 
if NumberUpdated the 
begin 
    LocalNumber = temp; 
end; 

이럴, 엄지 손가락의 기본 규칙 :
는 ".. 읽어 스레드 안전 쓰기가 스레드 안전하지 않습니다"
따라서 쓰기를 수행하려는 경우 모든 곳에서의 값을 읽으면 값을 읽을 수 있고 이면이 될 수 있습니다.
한편, 하나의 스레드에서만 값을 읽고 쓰면 스레드로부터 안전합니다. 따라서 임시 위치에 많은 양의 쓰기 작업을 수행 한 다음 응용 프로그램 전체 데이터의 업데이트를 동기화 할 수 있습니다.

보너스 광고 :

VCL은 스레드로부터 안전하지 않습니다. 주 스레드에서 UI의 모든 수정을 유지하십시오. 주 스레드에 모든 ui 항목의 생성도 유지하십시오.

많은 기능이 스레드 안전도 아니지만 다른 기능은 기본 winapi 호출에 의존하는 경우가 많습니다.

"목록 안전성"이 많은 것을 의미 할 수 있으므로 "목록"이 도움이되지 않을 것이라고 생각합니다.

+0

+1, 아주 좋은 점. 관심있는 사람은 http://en.wikipedia.org/wiki/Memory_barrier 및 링크 된 정보를 확인하십시오. – mghie

5

32 비트 아키텍처에서는 올바르게 정렬 된 32 비트 이하의 데이터 형식 만 원자 적이어야합니다. 32 비트 값은 4로 정렬되어야합니다 (데이터 주소는 4로 나눌 수 있어야합니다). 당신은 아마 그런 엄격한 레벨에서 인터리빙을하지 않을 것이지만, 이론적으로 두 배, Int64 또는 확장 비 원자 적 쓰기를 가질 수 있습니다.

+0

매우 오랜 시간 동안 32 비트 모드에서도 x86 하드웨어에서는 64 비트 읽기/쓰기가 기본 단위였습니다 –

+0

멀티 코어 및 멀티 프로세서 아키텍처가 도입 된 이후로 정렬은 더 이상 보장되지 않습니다. 나는 다른 스레드가 읽으려고하는 동안 적절히 정렬 된 데이터를 쓰는 극단적 인 짧은 스레드를 사용하여 이것을 증명했습니다. 독자는 작성한 값 집합에 읽지 않은 값이있을 경우 예외를 발생 시키도록 코드가 작성되었습니다. –

7

이것은 스레드로부터 안전한 데이터 유형의 문제는 아니지만, 사용자가 수행하는 작업에 대한 질문입니다. 잠금을 사용하지 않으면 아무 작업도 값을로드하고 변경 한 다음 다시 쓰는 스레드 세이프가됩니다. 숫자를 증가 시키거나 감소 시키거나 집합의 요소를 지우거나 설정하는 것은 스레드 안전하지 않습니다.

원자 연산을 허용하는 여러 가지 기능이 있습니다. 인터록 증가, 인터록 감소 및 인터록 교환입니다. 이것은 일반적인 개념으로, Windows, x86 또는 Delphi에만 국한된 것은 아닙니다. Delphi의 경우 Windows API의 InterlockedFoo() 함수를 사용할 수 있습니다. 여러 개의 래퍼도 있습니다. 또는 직접 작성하십시오. 이 함수는 정수에서 작동하므로 정수 증가 (32 비트)와 감소 (increment)가 가능합니다.

어셈블러와 접두사 ops에 잠금 접두사를 사용할 수도 있습니다.

자세한 내용은 this StackOverflow을 참조하십시오.

0

인디 코드 IdThreadSafe.pas의 일부 원자/스레드 안전 데이터 유형이 포함되어

  • TIdThreadSafeInteger
  • TIdThreadSafeBoolean을
  • TIdThreadSafeString
  • TIdThreadSafeStringList 좀 더 ...
1

멀티 코어 RISC 프로세싱 및 별도 코어 c 아픈 메모리가 현대 프로세서와 섞여 있기 때문에 '사소한'고수준 언어 읽기 또는 쓰기 구조 (또는 많은 경우 한 번에 한 번씩 8086 개의 '원자'어셈블리 명령어)은 원자 적으로 간주 될 수 있습니다. 실제로 어셈블러 명령어가 원자 적으로 설계된 경우가 아니라면 원자 단위가 아니며 메모리 판독을위한 대부분의 메커니즘을 포함합니다. 어셈블러 수준에서의 긴 정수 읽기조차도 동일한 메모리를 공유하고 RISC 프로세서 수준에서 비동기 캐시 업데이트 동작을 사용하는 다른 프로세서 코어에서 동시 쓰기로 인해 손상 될 수 있습니다. 여러 RISC 코어로 구성된 프로세서에서 어셈블리 언어 명령어조차도 사실상 "상위 레벨"코드 명령어라는 것을 기억하십시오! 비트 수준에서 어떻게 구현되고 있는지 알 수 없으며 이전의 8086 (단일 코어) 어셈블러 설명서를 읽는다면 기대했던 것과 다를 수도 있습니다. Windows는 네이티브 시스템 호환 원자 연산자를 제공하므로 원자 연산에 대한 기본 가정을 작성하는 대신 이러한 연산자를 사용하는 것이 좋습니다.

왜 Windows 운영자를 사용합니까? Windows가 수행하는 첫 번째 작업 중 하나가 실행중인 컴퓨터를 설정하기 때문입니다. 올바른 측면을 보장하는 핵심 요소 중 하나는 원자 적 연산이 무엇인지, 어떻게 작동하는지입니다. 미래의 프로세서에서 코드가 잘 작동하게하려면 코드에서이 모든 노력을 복제 (그리고 지속적으로 업데이트)하거나 Windows가 이미 시작했을 때 모든 작업을 이미 수행했는지 확인하십시오. 그런 다음 런타임에 필요한 코드를 API에 통합했습니다.

원자 적 조작에 대한 MSDN 페이지를 읽으십시오. Windows API가 이러한 기능을 제공합니다. 때로는 어색해 보이거나 어색한 것처럼 보일 수도 있지만, 미래의 증거이며 항상 주석으로 말한대로 정확하게 작동합니다.

어떻게 알 수 있습니까? 글쎄, 그들이하지 않으면 - 그럼 당신은 Windows를 실행할 수 없을 것입니다. 풀 스톱. 자신의 코드를 실행하는 데 신경 쓰지 마라.

코드를 작성할 때마다 항상 Parsimony을 이해하고 Occam's razor을 고려하는 것이 좋습니다. 즉, Windows가 이미이를 수행하고 있고 코드에 Windows가 있어야 실행될 수도 있고 작동하지 않을 수도있는 많은 대체 가능하고 점점 더 복잡한 가상의 솔루션을 시도하지 않고 Windows가 이미 수행 한 것을 사용하십시오. 그 밖의 일을하는 것은 물론 당신의 시간 낭비 일뿐입니다.

관련 문제