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.