2010-09-21 13 views
6

Alles, ich versuche, zwei gleichzeitige HttpWebRequests mit einer Methode ähnlich dem folgenden Code (in Pseudo-ish C# gezeigt) abzubrechen.Killing HttpWebRequest Objekt mit Thread.Abort

Die Main-Methode erstellt zwei Threads, die HttpWebRequests erstellen. Wenn der Benutzer dies wünscht, kann er die Anfragen durch Anklicken einer Schaltfläche abbrechen, die dann die Abort-Methode aufruft.

private Thread first; 
private Thread second; 
private string uri = "http://somewhere"; 

public void Main() 
{ 
    first = new Thread(GetFirst); 
    first.Start(); 

    second = new Thread(GetSecond); 
    second.Start(); 

    // Some block on threads... like the Countdown class 
    countdown.Wait(); 
} 

public void Abort() 
{ 
    try 
    { 
    first.Abort(); 
    } 
    catch { // do nothing } 

    try 
    { 
    second.Abort(); 
    } 
    catch { // do nothing } 
} 

private void GetFirst(object state) 
{ 
    MyHandler h = new MyHandler(uri); 
    h.RunRequest(); 
} 

private void GetSecond(object state) 
{ 
    MyHandler h = new MyHandler(uri); 
    h.RunRequest(); 
} 

Der erste Thread bekommt durch eine Socket unterbrochen:

A blocking operation was interrupted by a call to WSACancelBlockingCall 

Der zweite Faden hängt an GetResponse().

Wie kann ich beide Anfragen so abbrechen, dass der Webserver weiß, dass die Verbindung abgebrochen wurde? und/oder Gibt es einen besseren Weg, dies zu tun?

UPDATE

Wie bereits angedeutet, wäre eine gute Alternative sein BeginGetResponse zu verwenden. Ich habe jedoch keinen Zugriff auf das HttpWebRequest-Objekt - es ist in der Klasse MyHandler abstrahiert. Ich habe die Frage geändert, um dies zu zeigen.

public class MyHandler 
{ 
    public void RunRequest(string uri) 
    { 
    HttpWebRequest req = HttpWebRequest.Create(uri); 
    HttpWebResponse res = req.GetResponse(); 
    } 
} 
+0

Leider kann ich als Antwort darauf MyHandler nur ändern, wenn Sie darauf zugreifen können. Ansonsten ist der einzige Weg, um es zu töten, wie Sie es gerade tun. Alternativ können Sie ein Timeout mit der Option HttpWebRequest.Timeout angeben: http: //msdn.microsoft.com/en-us/library/system.net.httpwebrequest.timeout.aspx –

Antwort

4

Verwenden BeginGetResponse den Anruf zu initiieren und dann die Abort Methode für die Klasse verwenden, es zu löschen.

http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest_methods.aspx

Ich glaube Abort nicht mit den synchronen GetResponse arbeiten:

http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.abort.aspx

Wenn Sie mit der synchronen Version zu bleiben haben, um die Situation zu töten, alles, was Sie tun können, ist Abbruch der Faden. Aufgeben warten, können Sie ein Timeout angeben:

http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.timeout.aspx

Wenn Sie den Prozess töten müssen, ich würde behaupten, es in einem neuen AppDomain startet und Fallenlassen der AppDomain, wenn Sie die Anfrage töten wollen; anstatt einen Thread innerhalb des Hauptprozesses abzubrechen.

+1

Adam - Könnten Sie Ihren Kommentar näher erläutern? glaube, Abort funktioniert nicht mit der synchronen GetResponse "? Wo findest du das? – Armbrat

+0

Es wurde nicht explizit erwähnt, aber der Satz: "Die Abort-Methode führt synchron den Callback aus, der für die Methoden BeginGetRequestStream oder BeginGetResponse angegeben wurde, wenn die Abort-Methode aufgerufen wird, während eine dieser Operationen aussteht." führte mich dazu, anzudeuten, dass es nur für diese Methoden gültig war. Wenn Sie keinen Cross-Thread-Aufruf in Ihr HttpWebRequest-Objekt ausführen, können Sie Abort nicht aufrufen, da er derzeit bei GetResponse blockiert wird. –

3

Eine ThreadAbortException ist sehr unspezifisch. HttpWebRequest unterstützt bereits eine Möglichkeit, die Anforderung mit der Methode Abort() auf vorhersehbare Weise abzubrechen. Ich empfehle es stattdessen.

Beachten Sie, dass Sie weiterhin eine WebException für den Thread erhalten, die Ihnen mitteilen soll, dass die Anforderung extern abgebrochen wurde. Sei bereit, es zu fangen.

2

Dies liegt möglicherweise an .NET-Verbindungspooling. Jede WebRequest-Instanz verfügt über einen ServicePoint, der das Ziel beschreibt, mit dem Sie kommunizieren möchten (Serveradresse, Port, Protokoll, ...). Diese ServicePoints werden wiederverwendet. Wenn Sie also zwei WebRequests mit derselben Serveradresse, demselben Port und demselben Protokoll erstellen, teilen sie sich dieselbe ServicePoint-Instanz.

Wenn Sie WebRequest.GetResponse() aufrufen, verwendet es den Verbindungspool, der vom ServicePoint bereitgestellt wird, um Verbindungen zu erstellen.Wenn Sie dann den Thread mit Thread.Abort() beenden, gibt es die Verbindung zum Verbindungspool des ServicePoints NICHT zurück, daher denkt der ServicePoint, dass diese Verbindung noch verwendet wird. Wenn das Verbindungslimit des ServicePoint erreicht ist (Standard: 2), werden keine neuen Verbindungen erstellt, sondern es wird gewartet, bis eine der offenen Verbindungen zurückgegeben wird.

Sie können die Verbindungslimit wie folgt erhöhen:

HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url); 
httpRequest.ServicePoint.ConnectionLimit = 10; 

oder können Sie die Standardverbindungslimit verwenden, so dass jeder neue Servicepoint wird diese Grenze verwenden:

System.Net.ServicePointManager.DefaultConnectionLimit = 10; 

Sie auch Servicepoint verwenden können .CurrentConnections, um die Anzahl der offenen Verbindungen zu erhalten.

Sie könnten die folgende Methode verwenden Thread abbrechen:

private Thread thread; 
    private Uri uri; 

    void StartThread() 
    { 
     thread = new Thread(new ThreadStart(() => 
     { 
      WebRequest request = WebRequest.Create(uri); 
      request.ConnectionGroupName = "SomeConnectionGroup"; 

      var response = request.GetResponse(); 

      //... 
     })); 
     thread.Start(); 
    } 

    void AbortThread() 
    { 
     thread.Abort(); 
     ServicePointManager.FindServicePoint(uri).CloseConnectionGroup("SomeConnectionGroup"); 
    } 

Denken Sie daran, dass alle Verbindungen zum selben Server (oder Servicepoint), die die gleiche Verbindung Gruppennamen haben werden getötet werden. Wenn Sie mehrere gleichzeitige Threads haben, können Sie ihnen eindeutige Verbindungsgruppennamen zuweisen.