2010-12-30 3 views
1

Ich bin neu im Multithreading (und ein etwas Anfänger/Zwischenprogrammierer), und so kann mir etwas sehr offensichtlich fehlen. Ich arbeite gerade an einer Anwendung, die Daten von einem Server (einem Bloomberg-Server, um genau zu sein) ziehen und auch Geschäftslogik ausführen muss.C# - Multithreading mit einem Thread läuft Endlosschleife

Um Daten vom Server zu ziehen, müssen Sie eine Endlosschleife ausführen, um die Daten ständig zu aktualisieren. Gegenwärtig befindet sich der gesamte Daten-Pull-Code/Logik in einer separaten Klasse und hat eine Anzahl von öffentlichen Objekten (dataDict im Code unten), die mit den neuesten Daten aktualisiert werden.

Mein Gedanke war, den Daten ziehen Teil des Programms auf einem separaten Thread ausführen, und den Rest der Geschäftslogik auf dem Hauptthread ausführen. Wann immer die Geschäftslogik die neuesten Daten benötigte, konnte sie das Datenobjekt nur von dem anderen Thread (einem Wörterbuch) aufrufen. In Bezug auf den folgenden Code möchte ich, dass diese Klasse im Hintergrund ausgeführt wird und die Hauptgeschäftslogik das Dictionary, Object> dataDict bei Bedarf aufnimmt. Ich bin mir allerdings nicht sicher, wie ich das am besten mache.

Ich habe versucht, einen BackgroundWorker, aber das Problem, dass ich lief, war, dass, weil die Schleife endlos war ich den RunWorkerCompleted Ereignishandler feuern könnte, und der DoWork-Handler würde zu früh aufgerufen werden (es dauert einige Zeit für die Daten vollständig herunterladen).

Jede Hilfe würde sehr geschätzt werden !!

Zum Teil des Programms, der Code für die datapull zu erläutern ist (beachten Sie, ich hatte einige Bearbeitung zu tun, damit die Klammern/Klammern nicht perfekt sein können):

class BloombergSync 
{ 
    private Session mainSession = new Session(); 
    private List<String> contracts = new List<string>(); 
    private List<String> fields = new List<string>(); 
    public List<String> safeContracts = new List<string>(); 
    public List<String> safeFields = new List<string>(); 
    public Dictionary<Tuple<string, string>, Object> dataDict = new Dictionary<Tuple<string, string>, object>(); 
    private BackgroundWorker worker = new BackgroundWorker(); 


    { 

     while (true) 
     { 

      try 
      { 
       Event eventObj = mainSession.NextEvent(); 

       foreach (Message msg in eventObj.GetMessages()) 
       { 
        if (eventObj.Type == Event.EventType.SUBSCRIPTION_DATA) 
        { 
         Element dataElement = msg.AsElement; 

         //Figures out which contract the data is in reference to. 
         string topic = msg.TopicName; 

         // Then must add the data to the appropriate contract and field. 
         foreach (string field in fields) 
         { 
          if (dataElement.HasElement(field, true)) 
          { 
           // Collects data for the field 
           Element elm = dataElement.GetElement(field); 
           var dataPoint = elm.GetValue(); 

           // Have to figure out how to select first topic, and then the field within that topic. Has 
           // two keys (first topic, then field). 
           Tuple<string, string> tuple = new Tuple<string, string>(topic, field); 
           dataDict[tuple] = dataPoint; 
           worker.ReportProgress(1); 
          } 

         } 

        } 
        else 
        { 
         //Do Nothing if not data 
        } 
       } 
      } 
      catch (Exception ex) 
      { 
       System.Console.WriteLine("Got Exception:" + ex); 
      } 

     } 
    } 

Antwort

7

Klingt wie ein producer-consumer Szenario. Sie benötigen ein thread-safe Objekt (in Ihrem Fall eine Warteschlange) zum Übertragen von Daten.

In .NET 4.0 gibt es eine System.Collections.Concurrent die thread-safe ist und die Sie in Ihrem Projekt verwenden können. Dann kann der Hintergrundarbeiter Daten in CuncurrentQueue setzen und Verbraucher können Datenelemente aus der Warteschlange nehmen.

+0

Ich schaute nur darauf, und dachte, dass das der Weg sein könnte. Ich habe auch gerade festgestellt, dass ich das ProgressChanged-Ereignis verwenden und die überladene Methode für ReportProgress verwenden kann, um ein Objekt an den Ereignishandler zurückzugeben, wenn ich einen BackgroundWorker verwende. Das Objekt, das ich zurückgeschickt habe, war eine Klasse, die ich gebaut habe, um die drei Felder zu senden, die ich wollte ... Nicht sicher, ob das das effizienteste ist, aber es sieht so aus, als würde es funktionieren. – keynesiancross

+0

Die Art des Gewindes ist im ersten Schritt nicht dein Konstruktionsproblem. Entwerfen Sie das gemeinsame Datenmodell und wie Sie darauf "Thread-sicher" zugreifen können. Dann müssen Sie den Ausführungspfad verzweigen, damit der Haupt-Thread etwas tut und ein anderer Thread Daten erzeugt. Hauptthread (unter Verwendung eines gemeinsam genutzten Datenobjekts) kann Daten (falls verfügbar) abrufen und verwenden. Es empfiehlt sich, den Producer-Thread zu blockieren, wenn die Warteschlange voll ist. Das letzte, was Sie beachten sollten, ist, dass Sie einen normalen Thread oder Hintergrund benötigen, abhängig von Ihrem Fall. Zum Beispiel ist ohne Daten Hauptanwendung hat nichts zu tun, dann Hintergrundarbeiter ist keine gute Idee. – Xaqron