C #에서 bool 읽기 / 쓰기 원 자성입니다.


84

C #에서 bool 필드 원자에 액세스하고 있습니까? 특히, 나는 주위에 자물쇠를 두어야합니까?

class Foo
{
   private bool _bar;

   //... in some function on any thread (or many threads)
   _bar = true;

   //... same for a read
   if (_bar) { ... }
}


1
예,하지만 (아마도) 예입니다. 예, bool 필드에 액세스 / 설정하는 것은 원자 적이지만 if 작업은 그렇지 않으므로 (아래 Dror Helper의 답변 참조) 여전히 잠금이 필요할 수 있습니다.
JPProgrammer 2015 년

답변:


119

예.

bool, char, byte, sbyte, short, ushort, uint, int, float 및 reference 유형과 같은 데이터 유형의 읽기 및 쓰기는 원자 적입니다.

에서 발견 C # 언어 사양 .

편집 : 휘발성 키워드를 이해하는 것도 가치가있을 것입니다 .


10
재 할당하는 포인터 자체는 원자 적입니다 (예 : Foo foo1 = foo2;
user142350

4
@configurator : 질문은 정확히 무엇을 원하는지입니다. 잠금없는 프로그램이 잘못되기 쉽습니다. 따라서 실제로 필요하지 않는 한 더 간단한 프레임 워크 (예 : TPL)를 사용하는 것이 좋습니다. 다시 말해 '휘발성'은 잘못된 것이 아니라 까다로운 (즉 바람직하게는 피하는) 코드의 표시입니다. OP는 그가 원하는 것을 실제로 말하지 않았고, 나는 휘발성 윌리 닐리 를 추천 하는 것을 주저합니다 .
에이 몬 Nerbonne

4
와. 이것은 위험한 표현입니다. C ++ 사람들에게 atomic은 모든 읽기 쓰기가 해당 메모리 펜스로 둘러싸여 있음을 의미합니다. C #에서는 확실히 그렇지 않습니다. 그렇지 않으면 성능이 모든 변수 <long에 필수이기 때문에 끔찍할 것입니다. 여기 C # 용어에서 Atomic 은 읽기 또는 쓰기가 결국 발생할 때보 다 깨진 상태가 결코 보장되지 않음 을 의미하는 것 같습니다 . 그러나 "결국"이 언제인지에 대해서는 언급하지 않습니다.
v.oddou

4
int와 long에 쓰는 것이 원자 Interlocked.Add(ref myInt);적이 라면 언제 사용 합니까?
Mike de Klerk 2015

6
@MikedeKlerk 읽기와 쓰기는 원자 적이지만 별개입니다. i++i=i+1원자 읽기, 덧셈, 원자 쓰기를 수행함을 의미합니다. 다른 스레드 i는 읽기 후 쓰기 전에 수정할 수 있습니다. 예를 들어 i++동일한 i 에서 동시에 수행하는 두 개의 스레드는 동시에 읽을 수 있으며 (따라서 동일한 값을 읽음) 하나를 추가 한 다음 둘 다 동일한 값을 작성하여 효과적으로 한 번만 추가 할 수 있습니다. Interlocked.Add는 이것을 방지합니다. 일반적으로 유형이 원자 적이라는 사실은 쓰기 스레드가 하나 뿐이고 읽기 스레드가 많은 경우에만 유용합니다.
Jannis Froese

49

위에서 언급했듯이 bool은 원자 적이지만 여전히 원하는 작업에 따라 달라진다는 점을 기억해야합니다.

if(b == false)
{
    //do something
}

원자 연산이 아니므로 현재 스레드가 if 문 다음에 코드를 실행하기 전에 b 값이 변경 될 수 있습니다.


29

bool 액세스는 실제로 원자 적이지만 이것이 전체 이야기는 아닙니다.

'불완전하게 쓰여진'값을 읽는 것에 대해 걱정할 필요가 없습니다. 어떤 경우 에든 부울에 대해 의미 할 수있는 것이 무엇인지 명확하지 않습니다.하지만 적어도 다음과 같은 세부 정보가 있다면 프로세서 캐시에 대해 걱정해야합니다. 타이밍이 문제입니다. 코어 A에서 실행중인 스레드 # 1 _bar에 캐시가 있고 _bar다른 코어에서 실행중인 스레드 # 2에 의해 업데이트되는 경우, 스레드 # 1은 잠금을 추가 하거나 _bar로 선언 volatile하거나 명시 적으로 호출을 삽입 하지 않는 한 변경 사항을 즉시 볼 수 없습니다 Thread.MemoryBarrier(). 캐시 된 값.


1
"어쨌든 부울에 대해 의미 할 수있는 것은 분명하지 않습니다."전체 바이트가 동시에 기록되기 때문에 원자 단위로 1 바이트의 메모리에만 존재하는 항목. 여러 바이트에 존재하는 double과 같은 항목과 비교하여 한 바이트가 다른 바이트보다 먼저 기록 될 수 있으며 절반 기록 된 메모리 위치를 관찰 할 수 있습니다.
MindStalker 2010

3
MemoryBarrier ()는 프로세서 캐시를 무효화하지 않습니다. 일부 아키텍처에서는 프로세서가 성능을 위해 주 메모리에 대한 읽기 및 쓰기 순서를 변경할 수 있습니다. 단일 스레드의 관점에서 의미가 동일하게 유지되는 한 재정렬이 발생할 수 있습니다. MemoryBarrier ()는 프로세서에 재정렬을 제한하도록 요청하여 장벽 이전에 방출 된 메모리 작업이 장벽 뒤에서 끝나는 방식으로 재정렬되지 않도록합니다.
TiMoch 2013

1
메모리 장벽은 뚱뚱한 객체를 만들고 다른 스레드에서 읽을 수있는 참조를 전환 할 때 유용합니다. 장벽은 참조가 나머지 뚱뚱한 개체보다 먼저 주 메모리에서 업데이트되지 않도록 보장합니다. 다른 스레드는 fat 개체가 실제로 주 메모리에서 사용 가능하기 전에 참조 업데이트를 볼 수 없습니다. var fatObject = new FatObject(); Thread.MemoryBarrier(); _sharedRefToFat = fatObject;
TiMoch 2013

1

내가 사용한 접근 방식이 정확하다고 생각합니다.

volatile bool b = false;

.. rarely signal an update with a large state change...

lock b_lock
{
  b = true;
  //other;
}

... another thread ...

if(b)
{
    lock b_lock
    {
       if(b)
       {
           //other stuff
           b = false;
       }
     }
}

목표는 기본적으로 거의 발생하지 않는 다량의 상태 변경 정보를 제공하기 위해 잠글 필요가 있는지 확인하기 위해 매 반복마다 객체를 반복적으로 잠그는 것을 피하는 것이 었습니다. 내가 생각하는 이 방법을 사용할 수 있습니다. 그리고 절대적인 일관성이 필요하다면 휘발성이 b bool에 적절할 것이라고 생각 합니다.


4
이것은 일반적으로 잠금에 대한 올바른 접근 방식이지만 bool이 원자 적이면 잠금을 생략하는 것이 더 간단하고 빠릅니다.
dbkk

2
잠금이 없으면 "대형 상태 변경"이 원자 적으로 수행되지 않습니다. 잠금-> 설정 | check-> lock-> check 접근은 "// other stuff"코드보다 먼저 "// other"코드가 실행되도록합니다. "다른 스레드"섹션이 여러 번 반복된다고 가정하고 (제 경우에는) 대부분의 경우 부울을 확인하기
만하면

1
글쎄, 당신이 가지고 있다면 lock(), 당신은 필요하지 않습니다 volatile.
xmedeko
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.