333 messaggi dal 05 novembre 2012
Ciao Moreno,

grazie per le osservazioni, per me sono sempre spunto di riflessione e ben gradite...

Sicuramente uno sleep di 1 millisecondo allevierebbe il carico sulla CPU ma perché introdurre un polling di questo tipo se sai esattamente quando dovrà avvenire la prossima esecuzione?

Più che giusto

Dato che il codice che incrementa la variabile skipCount va in esecuzione su un thread separato a causa del Timer, la successiva istruzione addresses.Count > skipCount non è thread-safe e perciò potrebbe produrre risultati inaspettati.
Se si vuole usare un timer, bisognerebbe usare la classe Interlocked per incrementare e leggere il valore intero, dato che due thread insistono su di esso.

Chiedo venia, ho scritto il codice di fretta e non ho badato ad un eventuale race condition.

Prima in pausa pranzo tra un boccone e l'altro, non tenendo conto della soluzione che hai proposto (che continuo a preferire, concreta semplice e lineare) ho riscritto il metodo utilizzando la classe AutoResetEvent per interrompere l'esecuzione, sono partito con l'utilizzo della classe Interlocked ma mi sfugge qualcosa sulla parte di lettura, in che modo correggo "addresses.Count > skipCount" e Skip(skipCount)?

Un consiglio, secondo la tua esperienza...visto che il copia-incolla (ahime) è pratica molto diffusa, vale la pena modificare il post iniziale che ho scritto?

@pietro09
Sì ma solo a livello elementare. Lavoro maggiormente con le pagine web.

Se hai bisogno chiedi

Alessio
11.443 messaggi dal 09 febbraio 2002
Contributi

in che modo correggo "addresses.Count > skipCount" e Skip(skipCount)?


Ha senso usare interlocked solo nella condizione del while, dato che viene valutata su un thread separato. Lo Skip non ne ha bisogno perché si trova sullo stesso thread che scrive il valore.
while (addresses.Count > Interlocked.CompareExchange(ref skipCount, int.MinValue, int.MinValue))
{
 //...
}


Ecco un fiddle:
https://dotnetfiddle.net/75Xmnl
E un post in cui se ne parla:
https://stackoverflow.com/questions/24808291/reading-an-int-thats-updated-by-interlocked-on-other-threads


ho riscritto il metodo utilizzando la classe AutoResetEvent

Bene, AutoResetEvent elimina la necessità di fare il polling e perciò non serve neanche usare Interlocked.


Un consiglio, secondo la tua esperienza...visto che il copia-incolla (ahime) è pratica molto diffusa, vale la pena modificare il post iniziale che ho scritto?

Non ti preoccupare, lascialo com'è, non fa niente.

ciao ciao,
Moreno

Enjoy learning and just keep making
3.888 messaggi dal 28 gennaio 2003
Il codice che avete scritto è molto interessante anche al di fuori dell'uso che ne debbo fare io. Poi non è di tipo elementare, ma di tipo specialistico.
Detto questo penso che sia un bene proporlo rivisto e corretto, con le dovute spiegazioni, in modo da essere utilizzato dai frequentatori del forum.

Se qualcuno fa copia-incolla senza guardare minimamente il codice, fa solamente un danno a se stesso, perchè si priva del piacere e gusto di capire un codice ben fatto.

Comunque, grazie ancora. Ciao.

Pietro
11.443 messaggi dal 09 febbraio 2002
Contributi
Ciao Pietro,
è semplicemente un ciclo in cui metti uno sleep quando serve (cioè dopo 250 iterazioni).

Provo a riproporlo usando List, un ciclo for e metodi sincroni.
//Qui carichi i tuoi indirizzi
//GetEmailAddresses è un metodo che devi creare tu: io non so come recuperi l'elenco delle email
List<string> addresses = GetEmailAddresses();
//Ogni quante email dobbiamo mettere una pausa?
const int takeCount = 250;
//Quanto dobbiamo aspettare?
TimeSpan period = TimeSpan.FromMinutes(20);

for (int i = 0; i < addresses.Count; i++) {
  //Prendo l'email i-esima
  string address = addresses[i];
  //Gli scrivo un messaggio
  //SendEmail è un metodo che devi creare tu: io non so cosa devi scrivere
  SendEmail(address);
  //Se abbiamo inviato 250 email, allora ci mettiamo una pausa
  if (i+1 % takeCount == 0) {
   System.Threading.Thread.Sleep(period);
  }
}


Questa cosa la metti in un'applicazione console, non Winforms.
Non è utile usare WinForms dato che l'operazione non richiede l'intervento di un utente e perciò non devi mostrare alcuna UI grafica.

ciao,
Moreno
Modificato da BrightSoul il 06 febbraio 2019 09:19 -

Enjoy learning and just keep making
3.888 messaggi dal 28 gennaio 2003
Ti ringrazio, sempre gentilissimo.

Pietro
3.888 messaggi dal 28 gennaio 2003
Va bene, questo è il tuo codice tradotto per prova che funziona benissimo (ma che mi ha fatto vergognare del mio!)

Option Strict On

Module Module1

    Sub Main(args As String())
        Dim vs As String() = {"A", "B", "C", "D", "E", "F", "G", "H", "I"}

        Const intervallo As Integer = 4
        Dim period As TimeSpan = TimeSpan.FromSeconds(3)

        Dim v As String = ""
        Dim n As Integer = 0
        Dim sb As New StringBuilder()

        Try
            For i As Integer = 0 To vs.Count - 1
                v = vs(i)
                n = i

                'debug: simulo errore
                If i = 8 Then Throw New Exception("ERRORE")

                'SendEmail(v)
                Console.WriteLine("{0} {1}", i, v)
                sb.AppendFormat("{0} {1}", i, v) : sb.AppendLine()

                If ((i + 1) Mod intervallo) = 0 Then
                    System.Threading.Thread.Sleep(period)
                    Console.WriteLine()
                    sb.AppendLine()
                End If
            Next

        Catch ex As Exception
            Console.WriteLine("{0} {1} {2}", n, v, ex.Message)
            sb.AppendFormat("{0} {1} {2}", n, v, ex.Message)

        Finally
            File.WriteAllText("c:\tmp\log.txt", sb.ToString)
        End Try


        Console.Write("Premere un tasto per continuare . . . ") : Console.ReadKey()
    End Sub


End Module



Pietro
333 messaggi dal 05 novembre 2012
Ciao Moreno,

grazie per le indicazioni
Ha senso usare interlocked solo nella condizione del while, dato che viene valutata su un thread separato. Lo Skip non ne ha bisogno perché si trova sullo stesso thread che scrive il valore.
while (addresses.Count > Interlocked.CompareExchange(ref skipCount, int.MinValue, int.MinValue))
{
 //...
}


mi ero fissato sul metodo Interlocked.Read e non ho pensato all'utilizzo di CompareExchange con int.MinValue int.MaxValue, più andavo avanti e più vedevo la soluzione che ho proposto inizialmente come una forzatura...da qui ho riscritto tutto con la classe AutoResetEvent.

Il materiale che mi hai segnalato è stato utile ed in aggiunta ho dato una letta a questo articolo .NET Interlocked Operations

@pietro09

Nel codice che hai postato ho notato l'utilizzo di Console.WriteLine() ma sopratutto Console.ReadKey()
Attenzione. Se la tua console application gira nello scheduler di windows, i WriteLine sono superflui ed il ReadKey non fa terminare l'applicazione.

Hai due soluzioni e dipendono dalle tue esigenze

1) Se il tuo applicativo deve girare solo nello scheduler e non c'è iterazione con un utente, ma torna utile in fase di sviluppo avere indicazioni, racchiudi i WriteLine e ReadKey in blocchi di compilazione condizionale. Quando sviluppi compili in configurazione "Debug" quando rilasci in "Release" #If...Then...#Else Directives

#If DEBUG Then
   Console.Write("Premere un tasto per continuare . . . ")
   Console.ReadKey()
#End If


2) Altrimenti, una soluzione sicuramente più funzionale è gestire gli argomenti da riga di comando. Main() and command-line arguments

private static void Main(string[] args)
{
    bool quite = args.Length > 0 && args.First() == "q";

    // altro codice...

    if (!quite)
    {
        Console.WriteLine("Premere un tasto per continuare ...");
        Console.ReadKey();
    }
}


In questo esempio se richiami da CommandPrompt il tuo applicativo e passi "q" come parametro termina l'esecuzione senza aspettare iterazione da parte dell'utente. es. C:\MioProgetto\MiaApplicazione.exe q

(ma che mi ha fatto vergognare del mio!)


Mai vergognarsi...non si smette mai di imparare :-)

Ciao
Alessio
Modificato da scioCoder il 06 febbraio 2019 12:20 -

Alessio
3.888 messaggi dal 28 gennaio 2003
ok! ma quando vedo che due righe di codice ti risolvono il problema, e in modo elegante...

ho solo aggiunto una cosa:

If i < (vs.Count - 1) AndAlso ((i + 1) Mod intervallo) = 0 Then

in modo da non aspettare inutilmente period se ho già esaurito la scansione.

Grazie ancora

Pietro

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.