2010-05-17 12 views
10

Ich schreibe ein Programm, das auf einen eingehenden TcpClient hört und Daten verarbeitet, wenn es ankommt. Die Methode Listen() wird in einem separaten Thread in der Komponente ausgeführt, daher muss sie threadsicher sein. Wenn ich break aus einer dowhile Schleife, während ich in einer lock() Anweisung bin, werde die Sperre freigegeben werden? Wenn nicht, wie erreiche ich das?Was passiert, wenn Sie aus einer Lock() -Anweisung ausbrechen?

Danke!

(Jede andere Beratung zum Thema Asynchrone TCP Sockets ist auch willkommen.)

private void Listen() 
{ 
    do 
    { 
     lock (_clientLock) 
     { 
      if (!_client.Connected) break; 
      lock (_stateLock) 
      { 
       if (!_listening) break; 
       if (_client.GetStream().DataAvailable) HandleData(); 
      } 
     } 
     Thread.Sleep(0); 
    } while (true); 
} 
+6

Du gehst ins Gefängnis, dann wirst du freigelassen. – SwDevMan81

Antwort

20

Ja. Die lock-Anweisung wird in eine try/finally-Klausel übersetzt. In C# 4, beispielsweise eine lock-Anweisung wie folgt:

lock(obj) 
{ 
    // body 
} 

grob übersetzt (taken from Eric Lippert's blog here) zu:

bool lockWasTaken = false; 
var temp = obj; 
try 
{ 
    Monitor.Enter(temp, ref lockWasTaken); 
    { 
     // body 
    } 
} 
finally 
{ 
    if (lockWasTaken) 
     Monitor.Exit(temp); 
} 

Wenn die Ausführung verlässt den Umfang der lock {}, wird die zugrunde liegende Verriegelung gelöst werden automatisch. Dies geschieht unabhängig davon, wie Sie den Bereich verlassen (break/return/etc), da der Aufruf von Monitor.Exit intern in den finally-Block von try/finally eingeschlossen wird.

+0

Schneller als ich Sie sind :) –

+0

Dies funktioniert genauso, wenn Sie eine GOTO-Anweisung innerhalb einer Lock-Anweisung verwenden, um außerhalb der Sperre zu verzweigen, richtig? – eaglei22

1

Sobald Sie die lock{} verlassen, wird es entsperren, was Sie gesperrt haben (es ist wie eine using-Anweisung, dass betrachten). Es spielt keine Rolle, wo du austrittst (der Anfang, das Ende oder die Mitte), es ist, dass du den Umfang des Schlosses überhaupt verlassen hast. Denken Sie darüber nach, was passieren würde, wenn Sie in der Mitte eine Ausnahme machen würden.

+8

Sehr wichtig, darüber nachzudenken, was passieren würde, wenn Sie in der Mitte eine Ausnahme auslösen würden. Etwas Außergewöhnliches und Unerwartetes passiert mitten in einer kritischen Operation und was machst du als erstes? * Geben Sie die Sperre * frei, damit anderer Code in die Ressource eindringen kann, die während einer kritischen Operation eine Ausnahme ausgelöst hat! Die Tatsache, dass Sperren bei Ausnahmen freigegeben werden, bedeutet, dass Sie * mehr * Arbeit zu erledigen haben, nicht * weniger *. –

+0

Das stimmt, ich habe nicht so darüber nachgedacht. Das ist ein ausgezeichneter Punkt. Mein Gedanke war was, wenn er das Schloss nicht los ließ. Mit einer Ausnahme ist es unberechenbar, wo im Stapel es gefangen wird. Wenn jemand nicht vorsichtig war, könnte die Ausnahme mehrere Ebenen durchbrechen, in denen kein Hinweis darauf vorhanden ist, dass jemals eine Sperre verwendet wurde. Jetzt gibt es eine Ausnahme und ein gesperrtes Objekt, das "potentiell" andere erfolgreiche Operationen verhindert. – kemiller2002

+0

Das Problem ist, dass etwas schreckliches passieren kann, egal welche Strategie Sie wählen. Sperren und Ausnahmen mischen sich schlecht. Wenn wir billiges Software Transactional Memory hätten, wäre das weniger ein Problem; Wir könnten den Speicher einfach auf den Zustand zurücksetzen, in dem er sich vor dem Sperren befand. Aber wir haben dieses Tool noch nicht in unserer Toolbox. –

3

Ja, das Schloss wird freigegeben. Mit ILDASM oder Reflector können Sie den tatsächlich generierten Code betrachten. Die lock-Anweisung ist eine Abkürzung für den folgenden Code (grob).

Monitor.Enter(_client); 
try 
{ 
    // do your stuff 

} 
finally { 
    Monitor.Exit(_client); 
} 

Beachten Sie, dass der finally-Block immer ausgeführt wird.

+0

BTW - das ist richtig für <= C# 3, aber nicht ganz, was in C# 4 passiert. –

+0

Yep, dokumentiert Eric Lippert die Änderung in C# 4. http://blogs.msdn.com/ericlippert/archive/2009/ 03/06/locks-and-exceptions-do-not-mix.aspx – Haacked

+3

Beachten Sie, dass der finally-Block * not * immer ausgeführt wird. Der finally-Block wird nur ausgeführt * wenn die Kontrolle den try * verlässt. Kontrolle könnte den Versuch nicht verlassen; Der try-Block könnte eine Endlosschleife enthalten. Ein anderer Thread könnte den Prozess fehlschlagen. Ein Administrator kann den Prozess möglicherweise beenden. Ein Thread könnte die Stack-Guard-Seite zweimal hintereinander treffen und den Prozess herunterfahren. Jemand könnte das Gerät ausstecken. In all diesen Situationen wird der finally-Block nicht ausgeführt. –

1

Weil Sie um einen anderen Rat gefragt haben ... Ich habe bemerkt, dass Sie Schlösser schachteln. Dies ist an sich nicht unbedingt eine schlechte Sache. Aber es ist eine meiner roten Flaggen, auf die ich aufpasse. Es besteht die Möglichkeit eines Deadlocks, wenn Sie diese beiden Sperren in einer anderen Reihenfolge in einem anderen Teil Ihres Codes erhalten. Ich sage nicht, dass mit Ihrem Code etwas nicht in Ordnung ist. Es ist nur etwas anderes, auf das man achten sollte, weil man leicht falsch liegen kann.

+0

Danke für die Warnung! Ich sorge dafür, Objekte in der gleichen Reihenfolge zu sperren, so dass es nicht blockiert. – dlras2

0

Um die andere Hälfte Ihrer Frage zu beantworten:

Jede andere Beratung zum Thema Asynchrone TCP Sockets ist willkommen als auch

Einfach gesagt ich dies in der Art und Weise nicht schaffen würde demonstriert von Ihrem ursprünglichen Beitrag. Suchen Sie stattdessen in den Klassen System.Net.Sockets.TcpClient und System.Net.Sockets.TcpListener nach Hilfe. Verwenden Sie asynchrone Aufrufe wie BeginAcceptSocket (...) und BeginRead (...), und ermöglichen Sie dem ThreadPool, seine Aufgabe zu erledigen. Es ist wirklich ziemlich einfach, sich so zusammenzusetzen.

Sie sollten in der Lage sein, alle das Serververhalten ohne immer Sie wünschen zu erreichen Codierung die gefürchteten Worte „New Thread“ :)

Hier ist ein einfaches Beispiel von der Idee, minus der Idee des ordnungsgemäßen Herunterfahrens, Ausnahmebehandlung ect:

public static void Main() 
{ 
    TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Loopback, 8080)); 
    listener.Start(); 
    listener.BeginAcceptTcpClient(OnConnect, listener); 

    Console.WriteLine("Press any key to quit..."); 
    Console.ReadKey(); 
} 

static void OnConnect(IAsyncResult ar) 
{ 
    TcpListener listener = (TcpListener)ar.AsyncState; 
    new TcpReader(listener.EndAcceptTcpClient(ar)); 
    listener.BeginAcceptTcpClient(OnConnect, listener); 
} 

class TcpReader 
{ 
    string respose = "HTTP 1.1 200\r\nContent-Length:12\r\n\r\nHello World!"; 
    TcpClient client; 
    NetworkStream socket; 
    byte[] buffer; 

    public TcpReader(TcpClient client) 
    { 
     this.client = client; 
     socket = client.GetStream(); 

     buffer = new byte[1024]; 
     socket.BeginRead(buffer, 0, 1024, OnRead, socket); 
    } 

    void OnRead(IAsyncResult ar) 
    { 
     int nBytes = socket.EndRead(ar); 
     if (nBytes > 0) 
     { 
      //you have data... do something with it, http example 
      socket.BeginWrite(
       Encoding.ASCII.GetBytes(respose), 0, respose.Length, null, null); 

      socket.BeginRead(buffer, 0, 1024, OnRead, socket); 
     } 
     else 
      socket.Close(); 
    } 
} 

Für ein viel komplizierteres Beispiel dafür, wie dies die SslTunnel Library ich vor einer Weile geschrieben tun sehen.

Verwandte Themen