Frage

Was ist die richtige Technik zu haben, ThreadA Signal ThreadB eines Ereignisses, ohne ThreadB sitzt blockiert warten auf ein Ereignis geschehen?

ich habe einen Hintergrund-Thread, der eine gemeinsame Liste wird zu füllen. Ich versuche, einen Weg zu finden, asynchron den „main“ Thread zu signalisieren, dass es Daten abgeholt werden.


i als ein Ereignis mit einem Eventwaithandle-Objekt festlegen, aber ich kann meinen Haupt-Thread nicht habe an einem Event.WaitOne sitzen ().


i als einen Delegaten Rückruf haben, aber a) Ich will nicht den Haupt-Thread Arbeit in den Delegierten zu tun: der Faden muss wieder an die Arbeit, um mehr Sachen Zugabe - ich will es nicht warten, während die Delegierten ausführt, und b) die Delegierten werden muss, auf dem Hauptthread gemarshallt, aber ich bin nicht eine Benutzeroberfläche läuft, ich habe keine Kontrolle der Delegierten gegen .Invoke.


i betrachtet haben einen Delegierten Rückruf, der einfach startet einen Null-Intervall System.Windows.Forms.Timer (mit Gewinde Zugriff auf den Zeitgeber synchronisiert). Auf diese Weise der Faden muss nur geklebt werden, wie es ruft

Timer.Enabled = true;

, aber das scheint wie ein Hack.

In den alten Tagen mein Ziel wäre ein ausgeblendetes Fenster erstellt und hatte die Thread-Post-Nachrichten an, die versteckten Fenster HWND. i als eine versteckte Kontrolle zu schaffen, aber ich höre, dass Sie nicht auf einer Kontrolle ohne Griff erstellt .Invoke können. Plus, ich habe keine UI: mein Objekt auf einer Web-Server, Dienst oder Konsole erstellt worden sein könnte, ich will nicht, dass es eine grafische Kontrolle sein erscheint - noch habe ich eine Abhängigkeit von System.Windows kompilieren will. Formulare.


Ich hielt mit meiner Aufgabe, ein ISynchronizeInvoke Schnittstelle aussetzen, aber dann würde ich brauche .Invoke () zu implementieren, und das ist mein Problem.


Was ist die richtige Technik zu haben, ein Signal Thread B eines Ereignisses fädeln, ohne Gewinde B mit Warte sitzt blocked für ein Ereignis geschehen?

War es hilfreich?

Lösung

Hier ist ein Codebeispiel für die System.ComponentModel.BackgroundWorker Klasse.

    private static BackgroundWorker worker = new BackgroundWorker();
    static void Main(string[] args)
    {
        worker.DoWork += worker_DoWork;
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        worker.ProgressChanged += worker_ProgressChanged;
        worker.WorkerReportsProgress = true;

        Console.WriteLine("Starting application.");
        worker.RunWorkerAsync();

        Console.ReadKey();
    }

    static void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        Console.WriteLine("Progress.");
    }

    static void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        Console.WriteLine("Starting doing some work now.");

        for (int i = 0; i < 5; i++)
        {
            Thread.Sleep(1000);
            worker.ReportProgress(i);
        }
    }

    static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Console.WriteLine("Done now.");
    }

Andere Tipps

Ich bin die Kombination von ein paar Antworten hier.

Die ideale Situation verwendet einen Thread-safe-Flag wie etwa eine AutoResetEvent. Sie müssen nicht auf unbestimmte Zeit zu sperren, wenn Sie WaitOne() nennen, in der Tat ist es eine Überlastung hat, dass Sie ein Timeout angeben können. Diese Überlastung kehrt false wenn das Flag nicht während des Intervalls gesetzt wurde.

Ein Queue ist eine ideale Struktur für einen Erzeuger / Verbraucher-Beziehung, aber man kann es nachahmen, wenn Ihre Anforderungen Sie zwingen einen List zu verwenden. Der wesentliche Unterschied ist, Sie gehen Ihre Verbraucher Schlösser auf die Sammlung zugreifen müssen, um sicherzustellen, während es Elemente zu extrahieren; die sicherste Sache ist wahrscheinlich die CopyTo Methode zu verwenden, um alle Elemente zu einem Array zu kopieren, dann die Sperre freigeben. Natürlich sicherzustellen, dass Ihr Hersteller nicht gehalten werden versuchen, das List während der Sperre zu aktualisieren.

Hier ist eine einfache C # Konsolenanwendung, die zeigt, wie diese umgesetzt werden könnten. Wenn Sie mit den Zeitintervallen rumspielen können Sie verschiedene Dinge führen passieren; in dieser speziellen Konfiguration versuchte ich mehrere Artikel, bevor der Verbraucher prüft, ob Elemente der Produzent generieren zu haben.

using System;
using System.Collections.Generic;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        private static object LockObject = new Object();

        private static AutoResetEvent _flag;
        private static Queue<int> _list;

        static void Main(string[] args)
        {
            _list = new Queue<int>();
            _flag = new AutoResetEvent(false);

            ThreadPool.QueueUserWorkItem(ProducerThread);

            int itemCount = 0;

            while (itemCount < 10)
            {
                if (_flag.WaitOne(0))
                {
                    // there was an item
                    lock (LockObject)
                    {
                        Console.WriteLine("Items in queue:");
                        while (_list.Count > 0)
                        {
                            Console.WriteLine("Found item {0}.", _list.Dequeue());
                            itemCount++;
                        }
                    }
                }
                else
                {
                    Console.WriteLine("No items in queue.");
                    Thread.Sleep(125);
                }
            }
        }

        private static void ProducerThread(object state)
        {
            Random rng = new Random();

            Thread.Sleep(250);

            for (int i = 0; i < 10; i++)
            {
                lock (LockObject)
                {
                    _list.Enqueue(rng.Next(0, 100));
                    _flag.Set();
                    Thread.Sleep(rng.Next(0, 250));
                }
            }
        }
    }
}

Wenn Sie nicht an die Hersteller wollen überhaupt blockieren, ist es ein wenig komplizierter. In diesem Fall würde ich vorschlagen, den Erzeuger seine eigene Klasse machte beide mit einem privaten und einem öffentlichen Puffer und einem öffentlichen AutoResetEvent. Der Hersteller wird durch Standardspeicher Elemente im privaten Puffer, dann versucht sie die Öffentlichkeit Puffer zu schreiben. Wenn die Verbraucher mit dem öffentlichen Puffer arbeiten, setzt er die Flagge auf dem Hersteller-Objekt. Bevor der Hersteller versucht, Gegenstände aus dem privaten Puffer in dem öffentlichen Puffer zu bewegen, überprüft er diese Flagge und nur Kopien Elemente, wenn die Verbraucher nicht daran arbeiten.

Wenn Sie eine Background verwenden Sie den zweiten Thread zu starten und das Progress Ereignis verwenden, um den anderen Thread zu benachrichtigen, dass Daten bereit ist. Weitere Veranstaltungen sind ebenfalls vorhanden. diesem MSDN-Artikel sollten Sie begann .

Es gibt viele Möglichkeiten, dies zu tun, je nach genau das, was Sie tun mögen. Ein Erzeuger / Verbraucher-Warteschlange ist wahrscheinlich das, was Sie wollen. Für einen ausgezeichneten Blick in der Tiefe in Threads finden Sie im Kapitel über die Threading (online verfügbar) aus dem ausgezeichnetes Buch C # 3.0 in a Nutshell .

Sie können ein Autoreset (oder Manual). Wenn Sie AutoResetEvent.WaitOne verwenden (0, false), wird es nicht blockieren. Zum Beispiel:

AutoResetEvent ev = new AutoResetEvent(false);
...
if(ev.WaitOne(0, false)) {
  // event happened
}
else {
 // do other stuff
}

Die Background Klasse ist die Antwort in diesem Fall. Es ist das einzige Threading-Konstrukt, das asynchron in der Lage, Nachrichten an den Thread senden , die das Background Objekt erstellt. Intern BackgroundWorker verwendet die AsyncOperation Klasse durch die asyncOperation.Post() Methode aufrufen.

this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.asyncOperation.Post(delegateMethod, arg);

Ein paar andere Klassen im .NET Framework auch AsyncOperation verwenden:

  • Background
  • SoundPlayer.LoadAsync ()
  • SmtpClient.SendAsync ()
  • Ping.SendAsync ()
  • WebClient.DownloadDataAsync ()
  • WebClient.DownloadFile ()
  • WebClient.DownloadFileAsync ()
  • WebClient ...
  • PictureBox.LoadAsync ()

Wenn Ihr „Haupt“ Thread die Windows-Nachrichtensystem (GUI) Thread ist, dann können Sie eine Forms.Timer pollen - tune das Timerintervall je nachdem, wie schnell müssen Sie Ihre GUI-Thread ‚notice‘ haben die Daten aus der Arbeiter-Thread.

Denken Sie daran, den Zugriff auf den gemeinsam genutzten List<> zu synchronisieren, wenn Sie foreach verwenden werden, CollectionModified Ausnahmen zu vermeiden.

Ich verwende diese Technik für alle marktdatengesteuerte GUI-Updates in einer Echtzeit-Trading-Applikation, und es funktioniert sehr gut.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top