692 messaggi dal 11 febbraio 2013
ciao
in un progetto sto leggendo un database e poi scrivo in un altro (SQLite).

Usando Async/Await noto che il form si blocca.
In alcuni passaggi.

Credo che sia il metodo FillProdcuts

public partial class Product
    {
        private static readonly BP_SQLiteEntities _context = new BP_SQLiteEntities();
        private static IList<Product> _products = new List<Product>();
        public static async Task<IEnumerable<Product>> GetProductsAsync()
        {
            return await _context.Products.ToListAsync();
        }
        public static async Task AddProducts(IEnumerable<Product> products)
        {
            _context.Products.AddRange(products);
            await _context.SaveChangesAsync();
        }
        public static async Task RemoveProducts(IEnumerable<Product> products)
        {
            _context.Products.RemoveRange(products);
           await _context.SaveChangesAsync();
        }
        public static IEnumerable<Product> FillProducts(IEnumerable<Articoli> articoli)
        {
            foreach (var articolo in articoli)
            {
                var product = new Product
                {
                    Codice = articolo.Codart,
                    Descrizione = articolo.Descri,
                    PrezzoDettaglio = articolo.Prezzo,
                    ...
                };
                 _products.Add(product);
            } 
              
            return  _products;
        }


il codice lo eseguo da un click (dove poi inserirò altre tabelle)
vorrei qualche suggerimento su come procedo...sono un po confuso e visto che ho appena iniziato vorrei
proseguire nel modo piu corretto.

private async void button1_Click(object sender, EventArgs e)
        {
            //Articoli
            textBox1.Text = "Leggo Articoli da Database";
            var listaArticoli = await Articoli.GetArticoliAsync();
            var products = new List<Product>();
            textBox1.Text = "Leggo articoli da SQLiteDb";
            var articoli = await Product.GetProductsAsync();

            if (articoli.Any())
               await Product.RemoveProducts(articoli);

            textBox1.Text = "Aggiungo articoli a SQLiteDb";
            await Product.AddProducts(Product.FillProductsAsync(listaArticoli));

            textBox1.Text = "Fine";

        }

Modificato da jjchuck il 10 ottobre 2018 18.33 -
692 messaggi dal 11 febbraio 2013
Ho modificato il metodo cosi

public static async Task<IEnumerable<Product>> FillProductsAsync(IEnumerable<Articoli> articoli)
        {
            await Task.Run(() => {
                foreach (var articolo in articoli)
                {
                    var product = new Product
                    {
                        Codice = articolo.Codart,
                        Descrizione = articolo.Descri,
                        ...
                    };
                    _products.Add(product);
                }
            });                         
            return  _products;
        }


tuttavia non capisco perchè la UI si blocca e non vedo nella textbox tutte le info sullo stato dei task.

Non è semplice per me capire bene questi asincronismi
Modificato da jjchuck il 12 ottobre 2018 13.48 -
10.957 messaggi dal 09 febbraio 2002
Contributi
Ciao,
async/await è utile solo quando esegui operazioni che dipendono da periferiche di I/O come il disco e la rete. Ad esempio, SaveChangesAsync usa appunto la rete per collegarsi al database e poi il disco per persistere i dati. Perciò ha senso che sia async, perchè il thread principale non è attivamente impiegato per completare quell'operazione ma può svincolarsi per fare altro (es. aggiornare la UI) mentre le altre periferiche di I/O lavorano per completare l'operazione asincrona.

Questa operazione invece non fa affidamento su periferiche di I/O, perché consiste semplicemente di un ciclo foreach che manipola degli oggetti e necessita dell'intervento attivo del thread.
foreach (var articolo in articoli)
                {
                    var product = new Product
                    {
                        Codice = articolo.Codart,
                        Descrizione = articolo.Descri,
                        ...
                    };
                    _products.Add(product);
                }

Se il foreach lavora su una collezione di migliaia di elementi, allora impiegherà un certo tempo per completarsi e questo significa che il thread non potrà aggiornare la UI in quel frangente.
In questo specifico caso, è il caso di compiere l'operazione su un thread secondario, e questo l'hai fatto wrappando il codice con Task.Run:
 await Task.Run(() => {
   //...
 }


ciao,
Moreno

Enjoy learning and just keep making
692 messaggi dal 11 febbraio 2013
Il libro che seguo porta questo esempio in una app console.

static void Main(string[] args)
        {
            Console.WriteLine("before MetodoAsincrono");
            MetodoAsincrono();
            Console.WriteLine("after MetodoAsincrono");

            Console.ReadLine();
        }

        public static async void MetodoAsincrono()
        {
            Console.WriteLine("before await");
            await Task.Run(() =>
            {
                Console.WriteLine("start task");
                Thread.Sleep(3000);
                Console.WriteLine("end task");
            });
            Console.WriteLine("after await");
        }


...debbo ancora studiarlo bene perchè non avevo capito quello che mi scrivi

grazie per il chiarimento
ciao
10.957 messaggi dal 09 febbraio 2002
Contributi

non avevo capito quello che mi scrivi


Questo codice è bloccante per la UI e va eseguito su un altro thread.
foreach (var articolo in articoli)
            {
                var product = new Product
                {
                    Codice = articolo.Codart,
                    Descrizione = articolo.Descri,
                    PrezzoDettaglio = articolo.Prezzo,
                    ...
                };
                 _products.Add(product);
            } 


La maniera corretta di eseguire del codice su un altro thread è quella di usare Task.Run come hai fatto tu.

ciao,
Moreno

Enjoy learning and just keep making
692 messaggi dal 11 febbraio 2013
Grazie

tuttavia ora ho un dubbio.
nel testo si fa cenno alla classe Parallel e alla possibilità di eseguire cicli paralleli quando le iterazioni sono indipendenti.

Pensavo quindi di inserire un Parallel.ForEach nel metodo di cui sopra.

Però sto facendo delle prove qui a casa dove non ho il progetto per vedere la differenza tra un for e un for parallelo

Il tempo di esecuzione del loop con Parallel è piu alto. Dipende dalla Cpu del mio portatile?

var stopwatch = new Stopwatch();
            stopwatch.Start();

            Parallel.For(1, 1000, i => Console.WriteLine("{0}", i));
           
         //for (int i = 1; i < 1000; i++)
                       //    Console.WriteLine("{0}", i);
           
            stopwatch.Stop();
            Console.WriteLine("Time elapsed: {0}", stopwatch.ElapsedMilliseconds);
10.957 messaggi dal 09 febbraio 2002
Contributi

Dipende dalla Cpu del mio portatile?

No, dipende dal fatto che l'esempio che hai postato non è un buon candidato per la parallelizzazione.

Il metodo Parallel.For non è gratis ma introduce un piccolo costo prestazionale perché deve preparare un certo numero di thread e smistare il lavoro tra di essi. Questo costo prestazionale è quello che influisce in maniera negativa sui tempi di esecuzione del tuo esempio. Il problema in questo caso è che il lavoro è talmente semplice (solo un Console.WriteLine) che non vale la pena di parallelizzarlo, oltretutto perché i vari thread insistono sulla stessa risorsa (la Console, appunto) e vanno a contendersela quando devono scriverci.

Il costo introdotto da Parallel.For diventa del tutto trascurabile in altri casi, quando il carico lavoro è composto da calcoli intensivi per la CPU e quando c'è meno contesa di risorse.
Guarda il seguente esempio: ho usato un Thread.Sleep per simulare un carico di lavoro intensivo per la CPU che richiede 10ms a iterazione. Non c'è contesa di risorse perché non vado a scrivere nella console o ad aggiungere elementi ad una lista.
https://dotnetfiddle.net/5y9tlL
Nota come il tempo sia incredibilmente inferiore nel caso del Parallel.ForEach (l'80% in meno).

Quindi, i casi ideali per sfruttare il parallelismo sono quelli in cui fai calcoli intensivi (es. usare un algoritmo di derivazione su un insieme di password).

Il caso del foreach sugli articoli, secondo me, non è neanche lui un buon candidato per la parallelizzazione perché la costruzione di un oggetto non è un compito poi così intensivo per la CPU e soprattutto perché i risultati devono essere rimessi insieme in un'unica lista, che è una risorsa che i vari thread si andranno a contendere.
Inoltre, se usassi questa istruzione dentro un Parallel.For...
_products.Add(product);

Il programma si romperebbe perché il metodo Add NON è thread-safe. Servono collezioni thread-safe, tipo ConcurrentBag<T>.

ciao,
Moreno
Modificato da BrightSoul il 13 ottobre 2018 18.49 -

Enjoy learning and just keep making
692 messaggi dal 11 febbraio 2013
Immagino che le stesse considerazioni valgono per parallelLinq

Grazie davvero.
È chiaro che debbo rileggere e approfondire

Buon weekend
Ciao ciao

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.