2009-01-24 12 views
7
synchron verpacken

Ich habe eine Bibliothek von Drittanbietern, die eine Klasse enthält, die eine Funktion asynchron ausführt. Die Klasse erbt von der Form. Die Funktion führt grundsätzlich eine Berechnung anhand von in einer Datenbank gespeicherten Daten durch. Sobald es beendet ist, ruft es ein _Complete-Ereignis im aufrufenden Formular auf.Eine asynchrone Methode in C#

Was ich tun möchte, ist die Funktion synchron aufrufen, aber von einem nicht-Windows-Formular-Anwendung. Das Problem ist, egal was ich mache, meine Anwendungsblöcke und der _Complete-Event-Handler wird nie ausgelöst. Von einem Windows-Formular kann ich simulieren, die Funktion, die synchron läuft, indem ich ein "vollständiges" Kennzeichen und ein "while (! Vollständiges) application.doevents" benutze, aber offensichtlich ist application.doevents nicht verfügbar in einer non-windows Formularanwendung.

Gibt es etwas, das mich davon abhalten würde, die Methode der Klasse außerhalb einer Windows-Formularanwendung zu verwenden (aufgrund der Erben von 'Form')? Gibt es einen Weg, wie ich das umgehen kann?

Danke, Mike

+0

Ich bin nicht klar. Wird der Code ordnungsgemäß ausgeführt, wenn er in einer nicht Windows-Anwendung asynchron ausgeführt wird? Oder blockiert es immer unbegrenzt in einer Nicht-Windows-Anwendung? – Sam

+0

In einer Nicht-Windows-Anwendung blockiert es unbegrenzt, da das _Complete-Ereignis nie ausgelöst wird, obwohl es versucht hat, ManualResetEvents usw. zu verwenden. In einer Windows-Formularanwendung funktioniert es einwandfrei. – mikecamimo

Antwort

0

Haben Sie die Quelle für die Komponente haben? Es klingt, als ob es sich auf die Tatsache verlässt, dass es von einer WinForms-Umgebung aufgerufen wird (muss ein guter Grund sein, warum eine Bibliothek von Form erbt!), Aber es ist schwer sicher zu wissen.

8

Bei einem Stich könnte es sich lohnen, etwas wie das Folgende zu versuchen, das eine WaitHandle verwendet, um den aktuellen Thread zu blockieren, statt sich zu drehen und eine Markierung zu überprüfen.

using System; 
using System.Threading; 

class Program 
{ 
    AutoResetEvent _autoEvent; 

    static void Main() 
    { 
     Program p = new Program(); 
     p.RunWidget(); 
    } 

    public Program() 
    { 
     _autoEvent = new AutoResetEvent(false); 
    } 

    public void RunWidget() 
    { 
     ThirdParty widget = new ThirdParty();   
     widget.Completed += new EventHandler(this.Widget_Completed); 
     widget.DoWork(); 

     // Waits for signal that work is done 
     _autoEvent.WaitOne(); 
    } 

    // Assumes that some kind of args are passed by the event 
    public void Widget_Completed(object sender, EventArgs e) 
    { 
     _autoEvent.Set(); 
    } 
} 
+0

Ich glaube, er hat das nur in einer WinForms-Testumgebung gemacht (was funktionierte), ich denke, das Problem ist, dass die Operation nie abgeschlossen wird, wenn sie nicht in einer Winforms-App ist. Es ist also egal, wie lange Sie darauf warten gehen zu Ende :) –

+0

Ich vermute, dass der Fall sein kann, aber es ist immer noch wert, die Technik zu zeigen. Man weiß nie. :) – Kev

+1

Sicher .. eine weniger Person sitzt in einer Weile Schleife Hämmern der CPU ohne Grund ist eine gute Sache :-D –

1

Ich habe weitere Informationen zu diesem Problem (ich arbeite im gleichen Team wie mikecamimo).

Das Problem tritt auch in der Windows Forms-Anwendung auf, wenn es korrekt repliziert wird. In dem ursprünglichen OP trat das Problem in dem Windows-Formular nicht auf, da keine Blockierung vorhanden war. Wenn die Blockierung mithilfe eines ResetEvent eingeführt wird, tritt das gleiche Problem auf.

Dies liegt daran, dass sich der Ereignishandler (Widget_Completed) im selben Thread wie die Methode befindet, die Widget.DoWork aufruft. Das Ergebnis, dass AutoResetEvent.WaitOne(); blockiert für immer, da der Event-Handler nie aufgerufen wird, das Event zu setzen.

In einer Windows Forms-Umgebung kann dies umgehen, indem Application.DoEvents verwendet, um die Nachrichtenwarteschlange abzufragen und das Ereignis zu behandeln. Siehe unten.

using System; 
using System.Threading; 
using System.Windows.Forms; 

class Program 
{ 
    EventArgs data; 

    static void Main() 
    { 
     Program p = new Program(); 
     p.RunWidget(); 
    } 

    public Program() 
    { 
     _autoEvent = new AutoResetEvent(false); 
    } 

    public void RunWidget() 
    { 
     ThirdParty widget = new ThirdParty();     
     widget.Completed += new EventHandler(this.Widget_Completed); 
     data = null; 
     widget.DoWork(); 

     while (data == null); 
      Application.DoEvents(); 

     // do stuff with the results of DoWork that are contained in EventArgs. 
    } 

    // Assumes that some kind of args are passed by the event 
    public void Widget_Completed(object sender, EventArgs e) 
    { 
     data = e; 
    } 
} 

in einer nicht Windows Forms-Anwendung, wie ein Windows-Dienst, Anwendung nicht verfügbar ist, so DoEvents nicht aufgerufen werden kann.

Das Problem ist eines der Threading und das Widget.DoWork der zugehörige Event-Handler muss irgendwie in einem anderen Thread sein. Dies sollte verhindern, dass AutoResetEvent.WaitOne unbegrenzt blockiert. Ich denke ... :)

Alle Ideen, wie dies zu erreichen wäre fantastisch.

+0

Ich weiß das vor langer Zeit, aber Sie könnten versuchen, den Third-Party-Code in einem anderen Thread auszuführen, dh explizit einen neuen Thread zu starten, um ihn auszuführen. Auf diese Weise wird WaitOne auf einem anderen Thread blockieren, und das Completed-Ereignis wird (sollte) feuern. – Schmuli

1
AutoResetEvent _autoEvent = new AutoResetEvent(false); 

public WebBrowser SyncronNavigation(string url) 
{ 
    WebBrowser wb = null; 

    wb = new WebBrowser(); 
    wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted); 

    wb.ScriptErrorsSuppressed = true; 
    wb.Navigate(new Uri(url)); 

    while (!_autoEvent.WaitOne(100)) 
     Application.DoEvents(); 

    return wb; 
} 

void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) 
{ 
    //throw new NotImplementedException(); 
    _autoEvent.Set(); 
}