2017-06-22 2 views
0

Ich habe einen Multithreading-Algorithmus entwickelt, und ich habe einige Zweifel über die Freigabe eines Klassenmembers zwischen Threads in C#.C#: Freigeben eines Klassenmembers zwischen Threads

Nehmen wir an, wir haben zwei Klassen Algorithmus und Prozessor. Prozessor hat eine Hauptmethode DoWork und zusätzliche AvalableResources Methode, die gelegentlich aufgerufen wird, um die Anzahl der verfügbaren Ressourcen für die Verarbeitung zu ändern. Die Methoden Run und UpdateResources in Algorithm Objekt werden durch zwei verschiedenen Threads aufgerufen, möglicherweise auf verschiedenen Kernen arbeiten.

Ist es möglich, die variable _processor wird in CPU-Cache gespeichert werden und wird nie in den Speicher geladen werden, und AvaliableResources würde nie genannt werden, weil _processor für den zweiten Thread null ist?

class Processor 
{ 

    public void DoWork() { ... } 
    public void AvaliableResources(int x) { ... } 
} 

class Algorithm 
{ 
    private Processor _processor; 

    public void Run() 
    { 
     _processor = new Processor(); 
     _processor.DoWork(); 
     _processor = null; 
    } 

    public void UpdateResources(int x) 
    { 
     _processor?.AvaliableResources(x); 
    } 
} 

Wenn ein Synchronisationsproblem ist würde der folgende Code eine Lösung für sie sein?

Alternative 1

class Processor 
{ 
    public void DoWork() { ... } 
    public void UpdateResources(int x) { ... } 
} 

class Algorithm 
{ 
    private volatile Processor _processor; // added volatile keyword 

    public void Run() 
    { 
     _processor = new Processor(); 
     _processor.DoWork(); 
     _processor = null; 
    } 

    public void UpdateResources(int x) 
    { 
     _processor?.UpdateResources(x); 
    } 
} 

Alternative 2

class Processor 
{ 
    public void DoWork() { ... } 
    public void UpdateResources(int x) { ... } 
} 

class Algorithm 
{ 
    private Processor _processor; 

    public void Run() 
    { 
     _processor = new Processor(); 
     Thread.MemoryBarrier(); // Added memory barier 
     _processor.DoWork(); 
     _processor = null; 
    } 

    public void UpdateResources(int x) 
    { 
     Thread.MemoryBarrier(); // Added memory barier 
     _processor?.UpdateResources(x); 
    } 
} 

Edit: Wie Sie in den Kommentaren vorgeschlagen haben, lesen Sie besser erklärt Code:

class Processor 
    { 
     private int resources = Environment.ProcessorCount; 

     public void DoWork() 
     { 
      /*do some long running job using avaliable resources*/ 
     } 

     public void UpdateResources(int x) 
     { 
      resources = x; 
     } 
    } 

    class Algorithm 
    { 
     private volatile Processor _processor; 

     public void Run() 
     { 
      _processor = new Processor(); 
      _processor.DoWork(); 
      _processor = null; 
     } 

     public void UpdateResources(int x) 
     { 
      _processor?.UpdateResources(x); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var algorithm = new Algorithm(); 
      var result = Task.Run(() => algorithm.Run()); 
      // The resources were required for other staff and are reduced 
      algorithm.UpdateResources(1); 
      // The resources are reassigned for the long running algorithm 
      algorithm.UpdateResources(10); 
      // wait until the algorithm finishes 
      result.Wait(); 
      // this update should have no effect as the Run method has finished and _processor is null 
      algorithm.UpdateResources(10); 
     } 
    } 
+0

Was passiert, wenn Sie den Code ausführen? – Fabulous

+0

Beachten Sie auch, wenn https://stackoverflow.com/questions/3556351/why-we-need-thread-memorybarrier Ihre Anfrage – Fabulous

+0

"für den zweiten Thread" nicht adressiert - keiner der Threads ist der erste oder der zweite - sie sind einfach verschiedene Fäden; der "zweite" Thread könnte alles getan haben, bevor 'Run' aufgerufen wurde ... In diesem Fall: Was ist der Wert von' _processor'? –

Antwort

0

Die 2 Alternative ist falsch, wenn Sie auf diese Weise auf _processor obj zugreifen möchten.

In jedem Fall sollten Sie Null im Prozessor haben, weil DoWork schneller ausgeführt und beendet wird. Versuchen Sie, für die gleiche Sekunde den ersten Thread mit Schlaf suchen und prüfen, ob verfügbar ausgeführt wird.

Ihr Code enthält nicht genügend Informationen für den richtigen Weg.

Ist rechts volatile Schlüsselwort zu verwenden, aber wenn Run schnell verfügbar ist, ist nicht nie

Ok, gutes Beispiel

EDIT rufen. Aber das Problem ist der Hauptarbeiter mit Run fertig, bevor Sie UpdateResources ausgeführt wird.

Wenn Sie möchten, dass UpdateResources vor dem Warten zweimal ausgeführt wird, fügen Sie eine neue Methode ein, die nach Warten _processor= null ausführt. In gewisser Weise jeder mit diesem Code, jedes Mal, wenn Sie UpdateResources vor dem Warten aufrufen, ist Ihr Risiko _processor ist null.

Je nachdem, was Sie vor dem Warten wünschen

+1

'volatile' ist eine sehr subtile und nuancierte Biest; das meiste von dem, was Leute als "Effekt" von "volatil" denken, sind eigentlich nur sekundäre Nebenwirkungen und Implementierungsdetails. Jemanden zu fragen, was 'volatile' * eigentlich bedeutet * und wie das einer bestimmten Situation hilft, ist ... nun, abgesehen von ein paar CPU-Experten können die meisten Leute keine gute Antwort geben. Ich schließe mich in diese Gruppe von "unfähig, eine gute Antwort zu geben" –

+0

Ich verstehe nicht, was Sie mit diesem Thread wollen. Wenn Sie Tipps für Ihr Problem oder andere wünschen. Volatile wird verwendet, um immer den meisten Aktualisierungswert in Variabel zu haben. Dieses Schlüsselwort kommuniziert mit dem Compiler, um den Wert für den Multi-Thread-Zugriff zu optimieren. Wie auch immer, dein Code führt keine Update-Ressource aus, wenn die Ausführung beendet ist. Die Methode Update-Ressourcen wird nur aufgerufen, wenn während DoWork-Ausführung aufgerufen wird – Pasalino

+0

@Pasalino Ich habe das bessere Beispiel hinzugefügt, ich hoffe, es wird dazu beitragen. –

Verwandte Themen