2012-03-31 11 views
1

Ich habe viele Artikel gelesen sagen, dass IOCP in BeginXX/EndXX-Paaren verwendet wird. Wenn ich sie jedoch teste, zeigte mein Ergebnis, dass IOCP nicht im BeginExecuteReader-Aufruf funktionierte, während es im BeginGetResponse-Aufruf problemlos funktionierte.Warum IOCP nicht in BeginExecuteReader funktioniert

Ich bin sehr verwirrt mit diesem Ergebnis. Kann mir jemand den Grund sagen? Stimmt etwas mit meinem Testcode nicht?

Hier ist der Test unter:

  1. Test mit BeginGetResponse

    Code:

    public static void IoThread2() 
    { 
        ThreadPool.SetMinThreads(5, 3); 
        ThreadPool.SetMaxThreads(5, 3); 
        int w; int io; 
        ThreadPool.GetAvailableThreads(out w, out io); 
        int cid = Thread.CurrentThread.ManagedThreadId; 
        Console.WriteLine("Begin:" + w.ToString() + ";" + io.ToString() + "; id = " + cid.ToString()); 
        ManualResetEvent waitHandle = new ManualResetEvent(false); 
        WebRequest request = HttpWebRequest.Create("http://www.cnblogs.com/"); 
        AsyncCallback cb = new AsyncCallback(IOThread2CallBack); 
        request.BeginGetResponse(cb, request); 
        waitHandle.WaitOne(); 
    } 
    
    public static void IOThread2CallBack(IAsyncResult ar) 
    { 
    try 
    { 
        WebRequest request = (WebRequest)ar.AsyncState; 
        int w2; int io2; 
        ThreadPool.GetAvailableThreads(out w2, out io2); 
        int cid2 = Thread.CurrentThread.ManagedThreadId; 
        Console.WriteLine("End:" + w2.ToString() + ";" + io2.ToString() + "; id = " + cid2.ToString()); 
        var response = request.EndGetResponse(ar); 
        }catch (Exception ex){ } 
    } 
    

    Ergebnis:

    Begin:5;3; id = 10 
    End:5;2; id = 13 
    

    Ein IO-Thread verwendet wurde, den Rückruf auszuführen .

  2. Test mit BeginExecuteReader Code:

    public static void IoThread1() 
    { 
        ThreadPool.SetMinThreads(5, 3); 
        ThreadPool.SetMaxThreads(5, 3); 
        int w; int io; 
        ThreadPool.GetAvailableThreads(out w, out io); 
        int cid = Thread.CurrentThread.ManagedThreadId; 
        Console.WriteLine("Begin:" + w.ToString() + ";" + io.ToString() + "; id = " + 
            cid.ToString()); 
        SqlConnection connection = new SqlConnection(connectionString); 
        connection.Open(); 
        AsyncCallback da = new AsyncCallback(IoThreadCallBack); 
        SqlCommand command = new SqlCommand(s_QueryDatabaseListScript, connection); 
        IAsyncResult ir = command.BeginExecuteReader(da,        
              command,System.Data.CommandBehavior.CloseConnection); 
        ManualResetEvent waitHandle = new ManualResetEvent(false); 
        waitHandle.WaitOne(); 
    } 
    
    public static void IoThreadCallBack(IAsyncResult ar) 
    { 
    int w; int io; 
    ThreadPool.GetAvailableThreads(out w, out io); 
    int cid = Thread.CurrentThread.ManagedThreadId; 
    Console.WriteLine("End:" + w.ToString() + ";" + io.ToString() + "; id = " + 
            cid.ToString()); 
    SqlCommand command = (SqlCommand)ar.AsyncState; 
    StringBuilder sb = new StringBuilder(); 
    try 
    { 
         using (SqlDataReader reader = command.EndExecuteReader(ar)) 
         { 
          while (reader.Read()) 
          { 
           sb.Append(reader.GetString(0)).Append("; "); 
          } 
         } 
        } 
        catch (Exception ex){ } 
        finally 
        { 
         command.Connection.Close(); 
        } 
    } 
    

    Ergebnis:

    Begin:5;3; id = 10 
    End:4;3; id = 7 
    

    Ein weiteres Werk Thread verwendet wurde den Rückruf

Was ist das Problem ausführen?

+1

Ich bin verwirrt ... Wir * erwarten * der Rückruf an einem anderen Thread ausgeführt werden. Das ist weitgehend ** der ganze Punkt ** der Verwendung von Begin * ...? Hinweis: Wenn Sie einen Begin/End-Callback verwenden, um nur einen Wait-Handle zu öffnen, ist das sinnlos. Wenn Sie beim Aufrufer warten, können Sie Begin * ebenfalls nicht verwenden. –

+0

@MarcGravell, ich denke, die Frage ist, warum wurde ein Arbeiter-Thread im zweiten Fall und nicht ein IOCP verwendet. – svick

+2

E/A-Completion-Ports sind für einfache E/A-Anforderungen verfügbar, die durch einen überlappenden Read/WriteFile() - Aufruf gestartet werden. Es gibt nichts Einfaches an einer SQL-I/O-Anfrage, dazwischen sitzt ein großer Haufen Software, der "SQL Native Client". Sowie ein Stück C++/CLI-Code in System.Data.SqlClient. TDS ist das Protokoll, SNI ist die Schnittstelle, der Kanal kann Pipes, Sockets oder Shared Memory genannt werden. Alles ziemlich schlecht dokumentiert und unsichtbar zu zerlegen Augen. –

Antwort

0

Ihre Repro ist sehr kreativ. Dies ist ein interessanter Fall.

Versuchen Sie, 100 SqlCommands mit dem Befehlstext "WAITFOR DELAY '02: 00 ';" und beobachten Sie, wie sich die Anzahl der Threads im Laufe der Zeit entwickelt (starten Sie einen Timer, der die Statistik ausgibt).

Meine Vermutung ist, dass wir sehen, dass weit weniger als 100 Threads verbraucht werden (was bedeutet, dass dieser Teil des asynchronen Verhaltens funktioniert). Aus irgendeinem Grund verwendet SqlCommand die "falschen" Thread-Pool-Threads, aber dies reduziert die Skalierbarkeit möglicherweise überhaupt nicht.

+0

Was wir beobachtet haben, bedeutet nur, dass Ihr Callback auf einem Worker-Thread ausgeführt wurde. Aber die IO könnte noch als IOCP ausgeführt haben. Und das ist wichtig, weil Sie tausende herausragende Operationen ohne Tausende von Threads durchführen können. Bitte versuchen Sie dieses Experiment. – usr