private void RunAsync()
{
string param = "Hi";
Task.Run(() => MethodWithParameter(param));
}
private void MethodWithParameter(string param)
{
}
modificare
A grande richiesta devo notare che il Task
lancio verrà eseguito in parallelo con il thread chiamante. Assumendo l'impostazione predefinita, TaskScheduler
verrà utilizzato .NET ThreadPool
. Ad ogni modo, questo significa che è necessario tenere conto di qualsiasi parametro passato a Task
come potenzialmente accessibile da più thread contemporaneamente, rendendoli condivisi. Ciò include accedervi sul thread chiamante.
Nel mio codice sopra quel caso è del tutto discutibile. Le stringhe sono immutabili. Ecco perché li ho usati come esempio. Ma dì che non stai usando un String
...
Una soluzione è usare async
e await
. Questo, per impostazione predefinita, catturerà il SynchronizationContext
thread chiamante e creerà una continuazione per il resto del metodo dopo la chiamata a await
e lo collegherà al creatoTask
. Se questo metodo è in esecuzione sul thread della GUI WinForms, sarà di tipo WindowsFormsSynchronizationContext
.
La continuazione verrà eseguita dopo essere stata postata di nuovo nell'acquisizione SynchronizationContext
, di nuovo solo per impostazione predefinita. Quindi tornerai sul thread con cui hai iniziato dopo la await
chiamata. Puoi cambiarlo in vari modi, in particolare usando ConfigureAwait
. In breve, il resto di quel metodo non continuerà fino a dopo il Task
ha completato su un altro thread. Ma il thread chiamante continuerà a essere eseguito in parallelo, ma non il resto del metodo.
Questa attesa per completare l'esecuzione del resto del metodo può o non può essere desiderabile. Se nulla in quel metodo accede successivamente ai parametri passati al fileTask
potresti non volerlo utilizzare await
affatto.
O forse usi questi parametri molto più avanti nel metodo. Non c'è motivo di farlo await
immediatamente perché potresti continuare a lavorare in sicurezza. Ricorda, puoi memorizzare il Task
restituito in una variabile e await
su di essa successivamente, anche con lo stesso metodo. Ad esempio, una volta che è necessario accedere in modo sicuro ai parametri passati dopo aver svolto un po 'di altro lavoro. Anche in questo caso, non non è necessario await
sulla Task
destra quando lo si esegue.
Ad ogni modo, un modo semplice per rendere questo thread-safe rispetto ai parametri passati Task.Run
è farlo:
Devi prima decorare RunAsync
con async
:
private async void RunAsync()
Nota importante
Preferibilmente il metodo contrassegnato non dovrebbe restituire nullo, come menzionato nella documentazione collegata. L'eccezione comune a questo è gestori di eventi come i clic sui pulsanti e simili. Devono tornare nulli. Altrimenti cerco sempre di restituire un o quando uso . È una buona pratica per diversi motivi.async
Task
Task<TResult>
async
Ora puoi await
eseguire il Task
simile di seguito. Non puoi usare await
senza async
.
await Task.Run(() => MethodWithParameter(param));
Quindi, in generale, se si await
esegue l'attività, è possibile evitare di trattare i parametri passati come una risorsa potenzialmente condivisa con tutte le insidie di modificare qualcosa da più thread contemporaneamente. Inoltre, attenzione alle chiusure . Non li tratterò in modo approfondito, ma l'articolo collegato fa un ottimo lavoro.
Nota a margine
Un po 'fuori tema, ma fai attenzione a usare qualsiasi tipo di "blocco" sul thread della GUI di WinForms perché è contrassegnato con [STAThread]
. Utilizzandoawait
non bloccherà affatto, ma a volte lo vedo usato insieme a una sorta di blocco.
"Block" è tra virgolette perché tecnicamente non è possibile bloccare il thread della GUI di WinForms . Sì, se usi lock
il thread della GUI di WinForms continuerà a pompare messaggi, nonostante tu pensi che sia "bloccato". Non è.
Ciò può causare problemi bizzarri in casi molto rari. Uno dei motivi per cui non vuoi mai usare un lock
quando dipingi, per esempio. Ma questo è un caso marginale e complesso; tuttavia l'ho visto causare problemi folli. Quindi l'ho annotato per completezza.