2010-02-28 7 views
8

Ist ein C# -Struct Thread-sicher?Sind C# Strukturen sicher?

Zum Beispiel, wenn es ein:

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

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

in einer anderen Art:

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

ist Eigentum theData genannt, Thread-sicher?

+0

Kommt drauf an. Was machst du mit der Struktur? – SLaks

+2

Sie sollten auch keine veränderbare Struktur erstellen. http://stackoverflow.com/questions/441309/why-are-mutable-structs-evil – SLaks

+0

Nach diesem http://stackoverflow.com/questions/2353014/are-c-structs-thread-safe/2353051#2353051 Du solltest. –

Antwort

8

Nein, Strukturen in .NET sind nicht an sich Thread-sicher.

Die copy-by-value Semantik, die Strukturen haben jedoch große Bedeutung für diese Konversation.

Wenn Sie Ihre Strukturen übergeben und sie in irgendeiner Weise Variablen oder Pass-by-Value-Parametern zuweisen (keine ref- oder out-Schlüsselwörter), wird eine Kopie verwendet.

Dies bedeutet natürlich, dass alle Änderungen an der Kopie nicht in der ursprünglichen Struktur wiedergegeben werden, aber es ist etwas zu beachten, wenn Sie sie herumreichen.

Wenn Sie auf die Struktur direkt in einer Weise zugreifen, die keine copy-by-value-Semantik (z. B. Zugriff auf ein statisches Feld des Typs der Struktur und Marc Gravel points out in his answer, gibt es viele andere Möglichkeiten) mehrere Threads, dann muss man die Thread-Sicherheit der Instanz berücksichtigen.

+0

Und um es noch schlimmer zu machen, hängt es auch von einem * tollen * Deal ab, ob dein Code mit einem * Feld *, einer * Variable * oder einer * Eigenschaft * spricht. Glücklicherweise entfernt in diesem Fall die automatisch implementierte Eigenschaft ('TheData') die meisten davon. Ich erwähne es nur der Vollständigkeit halber; -p –

+0

Danke casperOne! Danke Marc! Wie caperOne es formuliert hat, dachte ich "die copy-by-value Semantik, die Strukturen haben große Relevanz für diese Konversation"; noch keine Anweisungen zu refrences zu sehen. Dieser Code ist nicht mein tatsächlicher Code, sondern eine Repräsentation. Ich schreibe einige Multithread-Anwendungen; und einige gebräuchliche Werkzeuge für parallele Programmierung sind C#; weil einige Muster häufig zurückkehren;) (Sicherlich werde ich hier nach Kritik suchen, wenn ich etwas Wertvolles herausgebracht habe). –

-2

Nein. Warum wäre es threadsicher? Es sind nur Daten. Es wird nicht durch Magie thread-sicher.

1

Ein struct ist nicht mehr Thread-Safe als ein gewöhnliches Feld oder eine Variable. Wenn Sie mindestens einen Thread haben, der diesen verändert, und mindestens ein weiterer Thread ihn gleichzeitig berührt, kann dies zu unerwartetem/undefiniertem Verhalten führen.

Auch veränderliche Strukturen sind Code Gerüche. Gibt es einen bestimmten Grund, warum Sie eine struct anstelle einer class benötigen? Benötigen Sie eine Werttyp-Semantik für diese Daten?

9

Nun - Best Practice ist, dass Strukturen (außer in einigen sehr spezifischen Szenarien, und auch dann in Gefahr) immer unveränderlich sein sollten. Und unveränderliche Daten sind immer Thread-sicher. Also, wenn Sie Best Practice gefolgt und machte dies:

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

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

dann ja; das ist Thread-sicher. In allen anderen Fällen lautet die Antwort "wahrscheinlich nicht".

Beachten Sie auch, dass die Unteilbarkeit Regeln gelten, so dass auch eine einzelne Lese- oder Update DadData.TheData kann nicht sein Thread-sicher, auch mit eine unveränderliche Struktur angenommen werden. Sie könnten (besonders für übergroße Strukturen) einen Thread haben, der die Struktur liest, während ein anderer Thread sie neu schreibt; ohne Synchronisation werden schlimme Dinge passieren (irgendwann).

0

Die direkten Lese- und Schreibvorgänge verschiedener Threads verschiedener Mitglieder einer veränderbaren Struktur stören sich nicht gegenseitig. Der Zugriff verschiedener Threads auf denselben Member über Interlocked-Methoden verhält sich entsprechend der Semantik dieser Methoden. Diese Fakten ermöglichen möglicherweise, dass veränderbare Strukturen ein Thread-sicheres Verhalten ermöglichen.

Veränderbare Speicherorte, die Strukturen enthalten, die keine Möglichkeit zur Mutation bieten, außer einem vollständigen Austausch, bieten keinerlei Thread-Sicherheit, außer in Fällen, in denen eine Struktur entweder eine einzelne 32-Bit-Ganzzahl oder einen einzelnen Objektverweis enthält Ein solcher (einteiliger) struct-Speicherort wird während des Schreibens garantiert vollständig alte Daten oder völlig neue Daten gelesen. Beachten Sie, dass es nicht möglich ist, eine der Interlocked-Methoden mit unveränderbaren Strukturen zu verwenden - auch solche Strukturen, die nur eine ganze Zahl oder Objektreferenz enthalten.

0

Nein, sind sie nicht. Ich habe eine sehr einfache App erstellt, um zu sehen, ob 10/10 Producer/Consumer-Threads auf dieselbe Strukturvariable zugreifen. Und schließlich wirst du Debugger.Break() sehen; wird getroffen werden. Bankguthaben sollte nie unter 0 gehen.

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()); 

     } 
    } 
}