843 messaggi dal 08 aprile 2009
Qual'è il corretto utilizzo di await Task.Run piuttosto che await?
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao Laura,
Task.Run serve in quelle situazioni in cui si deve eseguire del codice che richiede un uso intensivo della CPU. Il codice verrà eseguito su un altro thread, così che il thread della UI abbia la possibilità di aggiornare l'interfaccia. Questa tecnica sostituisce il vecchio BackgroundWorker o la creazione esplicita di oggetti Thread.

Task.Run restituisce un oggetto di tipo Task e quindi puoi usare await per attenderne il completamento, anche se sono poche le situazioni in cui ha senso farlo.
Infatti, se stai usando Task.Run per eseguire del codice intensivo per la CPU in un altro thread, non vuoi di certo usare await per bloccare il thread principale in attesa che si completi. Casomai, si usa il metodo .ContinueWith per eseguire del codice al termine del lavoro. Esempio:

Task.Run(() => CalcolaFattoriale(100)) //Codice intensivo per la CPU
     .ContinueWith((task) => Console.WriteLine("Fatto"));


Async/await invece li usi per operazioni che richiedono periferiche di I/O come il disco o la rete, come ad esempio l'invio di una query al database. Nel frangente in cui la query è in esecuzione sul database server, il thread dell'applicazione non fa nulla e può quindi essere riutilizzato per altre operazioni, tipo aggiornare la UI. Async/await non causano la creazione di altri thread, ma servono per il riutilizzo intelligente del thread, che è una risorsa preziosa per l'applicazione.

ciao,
Moreno

Enjoy learning and just keep making
843 messaggi dal 08 aprile 2009
Quindi creare un metodo async Task non è implicito il fatto che venga eseguito su un Thread separato ma dipende da come viene chiamato. Se viene richiamato con await Metodo viene eseguito sullo stesso Thread e l'interfaccia UI non viene aggiornata.

Ho dei punti in una applicazione che se eseguito in debug con punto di interruzione un metodo dichiarato async Task viene eseguito subito senza punto di interruzione sembra non terminare.
Oppure ho una srie di chiamate una dentro l'altra dove alla fine richiamo in await un plugin esterno che si occupa di connettersi al bluetooth di un device che se eseguito in debug con punto di interruzione si connette sempre ma se tolgo il punto di interruzione ci mette una vita...
11.886 messaggi dal 09 febbraio 2002
Contributi

Se viene richiamato con await Metodo viene eseguito sullo stesso Thread e l'interfaccia UI non viene aggiornata.

Sì sì, l'interfaccia può aggiornarsi, è questo il vantaggio. Il singolo thread comincia a eseguire le istruzioni che trova in Metodo e, quando incontra una parola chiave await può svincolarsi e fare altro, tipo aggiornare la UI.

Se invece non trova alcuna istruzione await, continuerà ad eseguire le istruzioni di Metodo e non avrà modo di svincolarsi per aggiornare la UI. E' per questo che Visual Studio ti dà un avviso quando usi aync in corrispondenza di un metodo che al suo interno non ha alcun await: il thread chiamante non potrà mai svincolarsi e l'esecuzione del metodo sarà sincrona, di fatto.

Gli await possono trovarsi solo in corrispondenza di metodi asincroni che non sono intensivi per la CPU.
Per analogia: per preparare un piatto di pasta, ci sono azioni che sono intensive per te (cioè richiedono il tuo intervento attivo) come prendere la pentola e accedere il gas, mentre altre non lo sono come portare l'acqua ad ebollizione perché c'è dell'hardware (il fornello) che lavora per te. Queste ultime sono le candidate ad essere operazioni asincrone su cui porre la parola chiave await.


Oppure ho una srie di chiamate una dentro l'altra dove alla fine richiamo in await un plugin esterno che si occupa di connettersi al bluetooth di un device che se eseguito in debug con punto di interruzione si connette sempre ma se tolgo il punto di interruzione ci mette una vita...

Il breakpoint non dovrebbe causare differenze nell'esecuzione ma posta un po' di codice...

Il metodo di connessione al bluetooth è sincrono o asincrono?
Modificato da BrightSoul il 16 ottobre 2018 14.22 -

Enjoy learning and just keep making
843 messaggi dal 08 aprile 2009
Provo a farti un albero di chiamate. Il metodo di chiamata alla connessione bluetooth è asincrono e trovandomi a sviluppare in Xamarin.Forms ho deciso di utilizzare un plugin esterno che è già funzionante su iOS (che conosco poco) e UWP. Io adesso ho un problema molto ma molto particolare su Android e ti faccio una premessa: noi ci colleghiamo al bluetooth per stampare in principal modo su stampanti a trasferimento termico portatili e qundo il cliente vuole le stampe in formato testo stampo direttamente io un file altrimenti se vogliono i moduli grafici genero io il PDF che poi do in pasto a un'app esterna; stiamo cominciando a vendere la nostra nuova versione (la vecchia era scritta sempre con Xamarin ma solo per Android); alcuni clienti lamentano il fatto che spesso il stampante e tablet perdono la connessione e quindi devono dissociare il dispositivo e riconnetterlo. Questo solo per tablet Samsung (almeno al momento). Io non sono mai riuscita a replicare l'errore fin quando un cliente ha mandato in assistenza un tablet per altri motivi e ne ho approfittato per fare delle prove e ho visto che questo accade quando il tablet lo riavvii. All'inizio ho pensato che fosse un problema di stampante e quindi ho perso giornate dietro a possibile configurazioni della stampante. Poi ho provato a stampare con l'app esterna e loro non hanno questo problema così come non lo ho con la nostra vecchia app che non utilizza metodo async Task. Da qui ho messo in discussione il plugin e il modo in cui io ho costruito le chiamate una dentro l'altra e soprattutto mi sono messa in discussione quando mettendo il punto di interruzione questo non accade.
Cmq la gerarchia delle chiamate è questa (ho tagliato molto codice quindi anche se alcune righe non hanno senso ce lo hanno invece)

        public static async Task<RETURN_CODE> StampaFile(string FileStampaGenerato,int StampaNumeroCopieGenerate)
        {
           
            object dialog = null;

            Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
            {
                dialog = ZIApp.AL.DialogService.ShowProgressDialog("Stampa in corso...");
            });

           

          
            RETURN_CODE return_stampa_file =  await ZI.BL.IManagers.StampaFilesManager.StampaFile(ZIApp.AL.ApplicationStatic.DBDitta, ZIApp.AL.ApplicationStatic.DBSistema, ZIApp.AL.ApplicationStatic.UserLogon, FileStampaGenerato, StampaNumeroCopieGenerate);


            ZIApp.AL.DialogService.CloseProgressDialog(dialog);
           
            return return_stampa_file;
        }
    
    
        public static async Task<RETURN_CODE> StampaFile(object DBContextDitta, object DBContextSystem, string UserLogon, string FileStampa,int NumeroCopie)
        {
            RETURN_CODE return_code = new RETURN_CODE();
            return_code.ResultOk = true;
            return_code.Message = "";

          

           return await StampaFileTXT(DBContextSystem, MetodoStampaTXT, FileStampa,NumeroCopie);

        }
    
        private static async Task<RETURN_CODE> StampaFileTXT(object DBContextSystem,ConfigurazioniManager.METODO_STAMPA_TXT metodo_stampa,string FileStampa, int NumeroCopie)
        {

            RETURN_CODE return_code = new RETURN_CODE();
            return_code.ResultOk = true;
            return_code.Message = "";

            await Task.Run(async() =>
                        {
                            return_code = await StampaFileBluetoothDirect(DBContextSystem, FileStampa, NumeroCopie);
                        });

            return return_code;

        }
    
        private static async Task<RETURN_CODE> StampaFileBluetoothDirect(object DBContextSystem, string FileStampa, int NumeroCopie)
        {

            RETURN_CODE return_code = new RETURN_CODE();
            return_code.ResultOk = true;
            return_code.Message = "";
            try
            {
                //devo cercare il device
                IBluetoothDevice device = null;
                using (var bluetooth = CrossBluetooth.Current)
                {
                    if (bluetooth.IsAvailable)
                    {

                        var lista = await bluetooth.GetPairedDevices();
                        device= lista.FirstOrDefault(f => f.Address == stampante_default.ADDRESS);

                    }
                }

                if (device == null)
                {
                    return_code.ResultOk = false;
                    return_code.Message = "Dispositivo " + stampante_default.name + " (" + stampante_default.ADDRESS + ") non trovata. Verificare l'associazione del dispositivo";
                    return return_code;
                }

                if (device.IsWriting)
                {
                    return_code.ResultOk = false;
                    return_code.Message = "La stampante " + stampante_default.name + " (" + stampante_default.ADDRESS + ") è già in scrittura!";
                    return return_code;
                }

                //mi connetto al device
                try
                {
                

           //PUNTO IN QUI SE METTO IL PUNTO DI INTERRUZIONE NON AVVIENTE
           //PRIMA RICHIAMAVO LA CONNESSIONE COSI':
           //await device.Connect();
                    await Task.Run(() =>
                    {
                        var task = device.Connect();
                        task.Wait();
                    });

                    if (!device.IsConnected)
                    {
                        await Task.Delay(500);
                        if (!device.IsConnected)
                        {
                            await Task.Run(() =>
                            {
                                var task = device.Connect();
                                task.Wait();
                            });

                            if (!device.IsConnected)
                            {
                                throw new Exception("Couldnt connect");

                            }
                        }
                    }





                }
                catch (Exception ex)
                {
                    return_code.ResultOk = false;
                    return_code.Message = "Impossibile connettersi al device: " + ex.Message;
                    return return_code;
                }

            
            }
            catch (Exception ex)
            {
                return_code.ResultOk = false;
                return_code.Message = "[StampaFileBluetoothDirect] Errore: " + ex.Message;
            }

            return return_code;
        }
    
    
    
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,


//PRIMA RICHIAMAVO LA CONNESSIONE COSI':
//await device.Connect();

Ok, andava bene in quel modo, perché poi hai cambiato?

Ecco alcuni cambiamenti che puoi fare. Il BeginInvokeOnMainThread qui non ci va, c'è un motivo che ti ha costretta a metterlo?
Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
            {
                dialog = ZIApp.AL.DialogService.ShowProgressDialog("Stampa in corso...");
            });

Metti semplicemente così, perché questo codice va in esecuzione nel thread della UI.
var dialog = ZIApp.AL.DialogService.ShowProgressDialog("Stampa in corso...");


Ovviamente il metodo StampaFile non deve essere eseguito con un Task.Run, ma semplicemente con await StampaFile.
Stessa cosa per tutti gli altri metodi asincroni. Butta via tutti i Task.Run a meno che tu non voglia effettivamente creare altri thread per svolgere del lavoro intensivo per la CPU.

Questo bloccone:
await Task.Run(() =>
                    {
                        var task = device.Connect();
                        task.Wait();
                    });

                    if (!device.IsConnected)
                    {
                        await Task.Delay(500);
                        if (!device.IsConnected)
                        {
                            await Task.Run(() =>
                            {
                                var task = device.Connect();
                                task.Wait();
                            });

                            if (!device.IsConnected)
                            {
                                throw new Exception("Couldnt connect");

                            }
                        }
                    }


Si può semplificare così:
int attempts = 3; //Proviamo a collegarci un massimo di 3 volte
do
{
  attempts--;
  if (attempts == 0)
  {
    throw new Exception("Couldn't connect");
  }

  await device.Connect();
  await Task.Delay(500);

} while (!device.IsConnected);

//Qui siamo connessi


Fammi sapere se hai errori.

ciao,
Moreno

Enjoy learning and just keep making

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.