2010-02-28 6 views
8

C# 구조체는 스레드로부터 안전한가요? 또 다른 유형의C# 구조체는 스레드로부터 안전합니까?

struct Data 
{ 
    int _number; 
    public int Number { get { return _number; } set { _number = value; } } 

    public Data(int number) { _number = number; } 
} 

: 예를 들어

가있는 경우

class DadData 
{ 
    public Data TheData { get; set; } 
} 

이 속성은 이름 TheData, 스레드 안전?

+0

에 달려 있습니다. 구조체로 뭐하고 있니? – SLaks

+2

또한 변경할 수있는 구조체를 만들면 안됩니다. http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil – SLaks

+0

이 내용에 따르면 http://stackoverflow.com/questions/2353014/are-c-structs-thread-safe/2353051#2353051 너해야 해. –

답변

8

아니요. .NET의 구조체는 본질적으로 스레드로부터 안전하지 않습니다.

그러나 구조화 된 값 별 의미 체계는이 변환과 관련성이 높습니다.

변수를 전달하거나 값을 전달하는 매개 변수 (ref 또는 out 키워드 없음)에 구조를 할당하는 경우 복사본이 사용되고 있습니다.

물론 이것은 복사본의 모든 변경 사항이 원본 구조에 반영되지 않았 음을 의미합니다.하지만이를 전달할 때주의해야 할 사항입니다.

값에 의한 복사 (예 : 구조의 유형 인 정적 필드에 액세스하고 Marc Gravel points out in his answer과 같이 다른 방법으로 많이 복사)와 같은 방식으로 구조에 직접 액세스하는 경우 여러 스레드를 사용하는 경우 인스턴스의 스레드 안전성을 고려해야합니다.

+0

그리고 이런 상황을 악화 시키려면 코드가 * field *, * variable * 또는 * property *와 대화하는지 여부에 따라 * 훌륭한 * 거래가 결정됩니다. 다행히도이 경우에는 자동 구현 된 속성 ('TheData')이 대부분을 제거합니다. 그래서 나는 완전성을 위해 그것을 단지 언급하고있다 .-p –

+0

감사합니다 casperOne! 고마워, 마크! casperOne이 말했듯이, 나는 구조가이 대화와 큰 관련이있는 "가치에 따른 복사 (copy-by-value semantics)"를 생각했다. 아직 refrences에 대한 지침을 보지 못했습니다. 이 코드는 제 실제 코드가 아니라 표현입니다. 다중 스레드 응용 프로그램을 작성 중입니다. 일부 일반적인 병렬 프로그래밍 도구는 C#입니다. 어떤 패턴이 자주 돌아 오기 때문에) (가치있는 것을 나왔을 때 나는 여기서 비판을 찾을 것이다.) –

-2

아니요. 왜 스레드로부터 안전합니까? 그것은 단지 데이터 일뿐입니다. 마법에 의해서는 쓰레드에 안전하지 않다.

1

struct은 일반 필드 또는 변수보다 스레드로부터 안전하지 않습니다. 적어도 하나의 스레드를 수정하고 동시에 어떤 식 으로든 적어도 하나의 스레드가 다른 스레드와 접촉하면 예기치 않은/정의되지 않은 동작이 발생할 수 있습니다.

또한 변경할 수있는 구조체는 코드 냄새입니다. class 대신 struct이 필요한 특별한 이유가 있습니까? 이 데이터에 가치 유형 의미론이 필요합니까?

9

글쎄요 - 가장 좋은 방법은 구조체가 항상 (몇 가지 매우 구체적인 시나리오를 제외하고, 심지어 위험에 처한 경우를 제외하고) 불변이어야한다는 것입니다. 그리고 불변의 데이터는 항상 스레드로부터 안전합니다. 따라서 우수 사례를 따랐고이를 작성한 경우 :

struct Data 
{ 
    readonly int _number; 
    public int Number { get { return _number; } } 

    public Data(int number) { _number = number; } 
} 

예, 그렇습니다. 스레드로부터 안전합니다. 다른 모든 경우에는 "아마도 그렇지 않습니다"라고 대답합니다.

참고 또한 자성 규칙이 적용되므로 DadData.TheData에 단 한 번의 읽기 또는 업데이트는 스레드 안전, 불변의 구조체와도 것으로 가정 할 수 없습니다. 다른 스레드가 다시 쓰는 동안 구조체를 읽는 하나의 스레드가있을 수 있습니다 (특히 특대 구조체의 경우). 동기화하지 않으면 나쁜 일이 일어날 것입니다 (결국).

0

가변 스레드 구조체의 서로 다른 멤버가 서로 다른 스레드의 직접 읽기 및 쓰기는 서로 간섭하지 않습니다. 인터 로킹 된 메서드를 통해 동일한 멤버에 대한 서로 다른 스레드의 액세스는 해당 메서드의 의미에 따라 작동합니다. 이러한 사실은 thread-safe 동작을 허용하도록 변경 가능한 구조체를 허용 할 수 있습니다.

철저한 대체를 제외하고 돌연변이의 수단을 제공하지 않는 구조체를 보유하고있는 변경 가능한 저장 위치는 구조체에 단일 32 비트 정수 또는 단일 객체 참조가있는 경우를 제외하고는 스레드 안전성을 제공하지 않습니다. 기록되는 것과 동시에 (단일 항목) 구조체 저장 위치가 완전히 이전 데이터 또는 완전히 새로운 데이터를 읽도록 보장됩니다. 단 하나의 정수 또는 객체 참조 만 포함하는 구조체 일지라도 변경 불가능한 구조체가있는 Interlocked 메소드를 사용할 수 없습니다.

0

아니요, 그렇지 않습니다. 10/10 생산자/소비자 스레드가 동일한 구조체 변수에 액세스하는지 확인하기 위해 매우 간단한 응용 프로그램을 만들었습니다. 그리고 결국에는 Debugger.Break(); 맞을거야. 은행 잔액은 절대로 0 이하가되어서는 안됩니다.

namespace StructThreadSafe 
{ 
    class Program 
    { 
     struct BankBalance 
     { 
      public decimal Balance { get; set; } 
     } 

     static void Main(string[] args) 
     { 
      BankBalance bankBalance = new BankBalance(); 
      bankBalance.Balance = 100; 
      List<Task> allTasks = new List<Task>(); 
      for (int q = 0; q < 10; q++) 
      { 
       Task producer = new Task(() => 
       { 
        for (int i = 0; i < 1000; i++) 
        { 
         if (bankBalance.Balance < 0) 
         { 
          if (Debugger.IsAttached) 
          { 
           Debugger.Break(); 
          } 
         } 
         bankBalance.Balance += 5; 
         Console.WriteLine("++Current Balance: " + bankBalance.Balance); 
         System.Threading.Thread.Sleep(100); 
        } 
       }); 
       allTasks.Add(producer); 
      } 
      for (int w = 0; w < 10; w++) 
      { 
       Task consumer = new Task(() => 
       { 
        for (int i = 0; i < 1000; i++) 
        { 
         if (bankBalance.Balance < 0) 
         { 
          if (Debugger.IsAttached) 
          { 
           Debugger.Break(); 
          } 
         } 
         if (bankBalance.Balance > 15) 
         { 
          bankBalance.Balance -= 15; 
          Console.WriteLine("--Current Balance: " + bankBalance.Balance); 
         } 
         else 
         { 
          Console.WriteLine("**Current Balance below minimum: " + bankBalance.Balance); 
         } 
         System.Threading.Thread.Sleep(100); 
        } 
       }); 
       allTasks.Add(consumer); 
      } 
      allTasks.ForEach(p => p.Start()); 
      Task.WaitAll(allTasks.ToArray()); 

     } 
    } 
} 
관련 문제