Task.Delay(250).Wait()
È sai si sta facendo qualcosa di sbagliato quando si utilizza Wait
nel codice che stai cercando di fare asincrona. Questo è un thread sprecato che non fa nulla.
Di seguito sarebbe molto meglio:
lastTask = lastTask.ContinueWith(t =>
{
// do some task
}).ContinueWith(t => Task.Delay(250)).Unwrap();
ContinueWith
restituisce una Task<Task>
, e la chiamata si trasforma Unwrap
che in un Task
che completerà quando il compito interna fa.
Ora, per rispondere alla tua domanda, diamo un'occhiata a ciò che il compilatore genera:
public void DoSomeTask()
{
if (this.lastTask == null)
this.lastTask = (Task) Task.FromResult<bool>(false);
// ISSUE: method pointer
// ISSUE: method pointer
this.lastTask = this.lastTask
.ContinueWith(
Program.<>c.<>9__2_0
?? (Program.<>c.<>9__2_0 = new Action<Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_0))))
.ContinueWith<Task>(
Program.<>c.<>9__2_1
?? (Program.<>c.<>9__2_1 = new Func<Task, Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_1))))
.Unwrap();
}
[CompilerGenerated]
[Serializable]
private sealed class <>c
{
public static readonly Program.<>c <>9;
public static Action<Task> <>9__2_0;
public static Func<Task, Task> <>9__2_1;
static <>c()
{
Program.<>c.<>9 = new Program.<>c();
}
public <>c()
{
base.\u002Ector();
}
internal void <DoSomeTask>b__2_0(Task t)
{
}
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
}
Questo è stato decompilato con dotPeek in "mi mostrano tutte le budella" modalità.
Guardate questa parte:
.ContinueWith<Task>(
Program.<>c.<>9__2_1
?? (Program.<>c.<>9__2_1 = new Func<Task, Task>((object) Program.<>c.<>9, __methodptr(<DoSomeTask>b__2_1))))
La funzione ContinueWith
viene dato un delegato Singleton. Quindi, non c'è chiusura su nessuna variabile lì.
Ora, c'è questa funzione:
internal Task <DoSomeTask>b__2_1(Task t)
{
return Task.Delay(250);
}
Il t
qui è un riferimento al compito precedente. Nota qualcosa? Non è mai usato La JIT contrassegnerà questo locale come irraggiungibile e il GC sarà in grado di pulirlo. Con le ottimizzazioni attivate, la JIT contrassegnerà in modo aggressivo i locali idonei per la raccolta, anche al punto che un metodo di istanza può essere eseguito mentre l'istanza viene raccolta dal GC, se il metodo di istanza non fa riferimento a this
nel codice rimasto da eseguire.
Ora, un'ultima cosa, c'è il campo m_parent
nella classe Task
, che non va bene per il tuo scenario. Ma finché non stai usando TaskCreationOptions.AttachedToParent
dovresti stare bene. È sempre possibile aggiungere il flag DenyChildAttach
per maggiore sicurezza e autodocumentazione.
Ecco il function which deals with that:
internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
{
return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
}
Quindi, si dovrebbe essere al sicuro qui. Se si desidera essere sicuro, eseguire un profiler di memoria su una lunga catena e vedere di persona.
Si può supporre che 'DoSomeTask()' non verrà chiamato in parallelo? –
@YacoubMassad Non influenzerebbe le domande specifiche a cui sta chiedendo, anche se lo fosse. – Servy
Qualche possibilità che tu possa usare 'async' /' await'? Il codice sarà molto semplice e più facile da ragionare. –