2015-11-15 10 views
6

Come posso verificare se un thread è stato restituito al pool di thread, utilizzando il debugger di VS C# 2015?Verifica se un thread è tornato al pool di thread

Ciò che è problematico nel mio caso è il fatto che non può essere rilevato eseguendo il debug linea per linea.

async Task foo() 
{ 
    int y = 0; 
    await Task.Delay(5); 
    // (1) thread 2000 returns to thread pool here... 
    while (y<5) y++; 
} 

async Task testAsync() 
{ 
    Task task = foo(); 
    // (2) ... and here thread 2000 is back from the thread pool, to run the code below. I want 
    // to confirm that it was in the thread pool in the meantime, using debugger. 
    int i = 0; 
    while (i < 100) 
    { 
     Console.WriteLine("Async 1 before: " + i++); 

    } 
    await task; 
} 

Nella prima linea di testAsync esecuzione sul filo 2000 foo è chiamato. Una volta che incontra await Task.Delay(5), il thread 2000 ritorna al pool di thread (presumibilmente, sto tentando di confermarlo) e il metodo attende il completamento di Task.Delay(5). Nel frattempo, il controllo ritorna al chiamante e il primo ciclo di testAsync viene eseguito anche sul thread 2000.

Quindi, tra due righe consecutive di codice, il thread è tornato al pool di thread e è tornato da lì. Come posso confermarlo con il debugger? Forse con la finestra del debugger di Threads?

Per chiarire un po 'più di quello che sto chiedendo: foo è in esecuzione sul filo 2000. Ci sono due possibili scenari:

  1. Quando colpisce await Task.Delay(5), filo 2000 restituisce al pool di thread per un tempo molto tempo breve, e il controllo ritorna al chiamante, alla riga (2), che verrà eseguito sul thread 2000 prelevato dal pool di thread. Se ciò è vero, non è possibile rilevarlo facilmente, poiché Thread 2000 si trovava nel pool di thread durante il periodo tra due righe consecutive di codice.

  2. Quando colpisce await Task.Delay(5), filo 2000 non tornare a infilare piscina, ma esegue immediatamente il codice in testAsync a partire dalla linea (2)

mi piacerebbe per verificare quale sta realmente accadendo .

+0

Provare 'attendere Task.Yield();' invece di 'attendere Task.Delay (5);'. –

+0

@IvanStoev nessuna differenza. Non cambia i thread su cui gira, o non si comporta in modo diverso nella finestra di debug dei thread. – user4205580

+5

Perché hai bisogno di sapere? – Enigmativity

risposta

3

C'è un grave errore nella tua ipotesi:

Quando colpisce attendono Task.Delay (5), filo 2000 restituisce al pool di thread

Dal momento che non si aspettano foo() ancora, quando il filo 2000 colpi Task.Delay(5) solo crea un nuovo Task e torna a testAsync() (a int i = 0;). Passa al blocco while e solo dopo attendi task. A questo punto, se task non è stato ancora completato, e supponendo che il resto del codice sia atteso, il thread 2000 tornerà al pool di thread.Altrimenti, se task è già completato, continuerà in modo sincrono da foo() (allo while (y<5) y++;).

EDIT:

che cosa se il metodo principale chiamato testAsync?

Quando chiamate di metodo sincroni e attende metodo asincrono, deve bloccare il filo se il metodo asincrono restituisce Task incompleta:

void Main() 
{ 
    var task = foo(); 
    task.Wait(); //Will block the thread if foo() is not completed. 
} 

noti che nel caso precedente il filo non restituisce al pool di thread - è completamente sospeso dal sistema operativo.

Forse si può dare un esempio di come chiamare testAsync in modo che thread 2000 ritorni al pool di thread?

Presupposto thread 2k è il thread principale, non può tornare al pool di thread. Ma è possibile utilizzare Task.Run(()=> foo()) per eseguire foo() nel pool di thread e poiché il thread chiamante è il thread principale, un altro thread pool di thread prenderà quell'attività. Così il seguente codice:

static void Main(string[] args) 
{ 
    Console.WriteLine("main started on thread {0}", Thread.CurrentThread.ManagedThreadId); 
    var testAsyncTask = Task.Run(() => testAsync()); 
    testAsyncTask.Wait(); 
} 
static async Task testAsync() 
{ 
    Console.WriteLine("testAsync started on thread {0}", Thread.CurrentThread.ManagedThreadId); 
    await Task.Delay(1000); 
    Console.WriteLine("testAsync continued on thread {0}", Thread.CurrentThread.ManagedThreadId); 
} 

Prodotto (sul mio PC) il seguente output:

main started on thread 1 
testAsync started on thread 3 
testAsync continued on thread 4 
Press any key to continue . . . 

Fili 3 e 4 è venuto da e restituito al pool di thread.

+0

se l'attività non è stata ancora completata e presupponendo che sia atteso il resto del codice - ho pensato che se l'attività non è ancora completata, il controllo tornerà al chiamante, su Thread 2000. Se è atteso 'testAsync', allora tornerà ancora un altro livello, anche su thread 2000. – user4205580

+0

@ user4205580 ** "Ho pensato che se l'attività non è stata ancora completata, il controllo tornerà al chiamante" ** - lo fa! ** "Se testAsync è atteso, verrà restituito un altro livello" ** - non esattamente. Quando il thread 2000 ritorna a 'testAsync' dopo che ha colpito' attende Task.Delay (5); 'in' foo() ', continua l'esecuzione sincrona di' testAsync' fino a quando non è necessario 'attendere' qualcosa. La prima volta 'testAsync' attende qualsiasi cosa, è dopo il blocco' while'. –

+0

Sì, e dopo aver premuto 'Attendi task', tornerà a' callerOfTestAsync' ed eseguirlo su thread 2000, quindi thread 2000 non ritorna al pool di thread. Bene, l'ho appena provato. – user4205580

3

È possibile stampare Thread.CurrentThread.ManagedThreadId nella console. Si noti che il pool di thread è libero di riutilizzare lo stesso thread per eseguire continuazioni su di esso, quindi non c'è alcuna garanzia che sarà diverso:

void Main() 
{ 
    TestAsync().Wait(); 
} 

public async Task FooAsync() 
{ 
    int y = 0; 
    await Task.Delay(5); 
    Console.WriteLine($"After awaiting in FooAsync: 
         {Thread.CurrentThread.ManagedThreadId }"); 

    while (y < 5) y++; 
} 

public async Task TestAsync() 
{ 
    Console.WriteLine($"Before awaiting in TestAsync: 
     {Thread.CurrentThread.ManagedThreadId }"); 
    Task task = foo(); 
    int i = 0; 
    while (i < 100) 
    { 
     var x = i++; 
    } 

    await task; 
    Console.WriteLine($"After awaiting in TestAsync: 
         {Thread.CurrentThread.ManagedThreadId }"); 
} 

Un'altra cosa che puoi controllare è ThreadPool.GetAvailableThreads per determinare se un altro lavoratore è stato distribuito per l'uso:

async Task FooAsync() 
{ 
    int y = 0; 
    await Task.Delay(5); 

    Console.WriteLine("Thread-Pool threads after first await:"); 
    int avaliableWorkers; 
    int avaliableIo; 
    ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo); 
    Console.WriteLine($"Available Workers: { avaliableWorkers}, 
         Available IO: { avaliableIo }"); 

    while (y < 1000000000) y++; 
} 

async Task TestAsync() 
{ 
    int avaliableWorkers; 
    int avaliableIo; 
    ThreadPool.GetAvailableThreads(out avaliableWorkers, out avaliableIo); 

    Console.WriteLine("Thread-Pool threads before first await:"); 
    Console.WriteLine($"Available Workers: { avaliableWorkers}, 
         Available IO: { avaliableIo }"); 
    Console.WriteLine("-------------------------------------------------------------"); 

    Task task = FooAsync(); 

    int i = 0; 
    while (i < 100) 
    { 
     var x = i++; 
    } 

    await task; 
} 

Sulla mia macchina, questo rendimenti:

Thread-Pool threads before first await: 
Available Workers: 1023, Available IO: 1000 
---------------------------------------------- 
Thread-Pool threads after first await: 
Available Workers: 1022, Available IO: 1000 
+0

Questo è quello che sto facendo attualmente, e sia la console.WriteLine stampa ** lo stesso ID di thread ** - perché probabilmente il thread 2000 è ritornato al pool di thread in attesa di Task.Delay (5) 'e quindi è stato ritirato dal pool di thread per eseguire il primo ciclo di' testAsync'. . Tuttavia, voglio controllare ** if ** il thread restituito al pool di thread. – user4205580

+0

@ user4205580 Perché pensi che non stia tornando? Dove andrà quel thread? Non hai modo di ripetere i thread all'interno del pool di thread, che è un dettaglio di implementazione. –

+0

Ho aggiunto commenti al codice. Voglio solo sapere se restituisce ** a ** il pool di thread o meno. Non è stato necessario tornare al pool di thread, giusto? Non lo sappiamo. – user4205580

1

Mi piacerebbe verificare quale sta realmente accadendo.

Non c'è modo di "verifica" che con debugger, perché il debugger è fatta per simulare la logica (sincrono) Flusso - vedi Walkthrough: Using the Debugger with Async Methods.

Per capire cosa sta succedendo (FYI è il tuo caso (2)), è necessario imparare await opere a partire dal Asynchronous Programming with Async and Await - Cosa succede in un sezione metodo asincrono, Control Flow in Async Programs e molte altre fonti.

Guardate questo frammento:

static void Main(string[] args) 
{ 
    Task.Run(() => 
    { 
     // Initial thread pool thread 
     var t = testAsync(); 
     t.Wait(); 
    }); 
    Console.ReadLine(); 
} 

Se facciamo la lambda per essere async e utilizzare await t; invece di t.Wait();, questo è il punto in cui il filo iniziale sarà restituito al pool di thread. Come ho detto sopra, non è possibile verificarlo con il debugger. Ma guarda il codice sopra e pensa logicamente - stiamo bloccando il thread iniziale, quindi se non fosse gratuito, i tuoi metodi testAsync e foo non potranno riprendere. Ma lo fanno, e questo può essere facilmente verificato inserendo il punto di interruzione dopo le lineeawait.