Domanda

Mi dispiace che il titolo sia un po 'schifoso, non sono riuscito a esprimerlo correttamente.

Modifica: dovrei notare che questa è un'app c # della console

Ho prototipato un sistema che funziona così (questo è un pseudo-codice approssimativo):

var collection = grabfromdb();

foreach (item in collection) {
    SendAnEmail();
}

SendAnEmail:

SmtpClient mailClient = new SmtpClient;
mailClient.SendCompleted += new SendCompletedEventHandler(SendComplete);
mailClient.SendAsync('the mail message');

SendComplete:

if (anyErrors) {
    errorHandling()
}
else {
    HitDBAndMarkAsSendOK();    
}

Ovviamente questa configurazione non è l'ideale. Se la raccolta iniziale ha, diciamo, 10.000 record, allora sta per rinnovare 10.000 istanze di smtpclient in un ordine abbastanza breve il più rapidamente possibile attraverso le righe - e probabilmente aspirare nel processo.

Il mio fine gioco ideale è avere qualcosa come 10 e-mail simultanee in uscita contemporaneamente.

Mi viene in mente una soluzione caotica: aggiungi un contatore, che aumenta quando viene chiamato SendAnEmail () e diminuisce quando viene inviato SendComplete. Prima che SendAnEmail () venga chiamato nel ciclo iniziale, controlla il contatore, se è troppo alto, quindi dormi per un breve periodo di tempo e poi ricontrolla.

Non sono sicuro che sia un'ottima idea, e immagino che la mente dell'alveare SO avrebbe un modo per farlo correttamente.

Ho pochissima conoscenza del threading e non sono sicuro che sarebbe un uso appropriato qui. Ad esempio, l'invio di e-mail in un thread in background, controlla innanzitutto il numero di thread figlio per assicurarti che non ne vengano utilizzati troppi. O se esiste un qualche tipo di "limitazione del thread" incorporato.


Aggiornamento

Seguendo i consigli di Steven A. Lowe, ora ho:

  • Un dizionario contenente le mie e-mail e una chiave univoca (questa è l'e-mail que
  • Un metodo FillQue, che popola il dizionario
  • Un metodo ProcessQue, che è un thread in background. Controlla la coda e SendAsycs qualsiasi e-mail nella coda.
  • Un delegato SendCompleted che rimuove l'e-mail dalla coda. E chiama nuovamente FillQue.

Ho qualche problema con questa configurazione. Penso di aver perso la barca con il thread in background, dovrei generare uno di questi per ogni elemento nel dizionario? Come posso fare in modo che il thread si "aggiri" per mancanza di una parola migliore, se l'email svuota il thread finisce.


aggiornamento finale

Ho inserito un 'while (true) {}' nel thread in background. Se la coda è vuota, attende alcuni secondi e riprova. Se la coda è ripetutamente vuota, interrompo il tempo e il programma termina ... Funziona bene. Sono un po 'preoccupato per il business "while (true)" però ...

È stato utile?

Soluzione

Risposta breve

Utilizza una coda come buffer finito, elaborata dal proprio thread.

Risposta lunga

Chiama un metodo di compilazione per creare una coda di e-mail, limitata a (diciamo) 10. Riempila con le prime 10 e-mail non inviate. Avvia un thread per elaborare la coda: per ogni e-mail nella coda, invialo asincrono. Quando la coda è vuota, dormire per un po 'e ricontrollare. Chiedi al delegato del completamento di rimuovere l'e-mail inviata o errata dalla coda e di aggiornare il database, quindi chiama il metodo di compilazione per leggere più e-mail non inviate nella coda (backup fino al limite).

Avrai solo bisogno di blocchi intorno alle operazioni della coda e dovrai solo gestire (direttamente) un thread per elaborare la coda. Non avrai mai più di N + 1 thread attivi contemporaneamente, dove N è il limite di coda.

Altri suggerimenti

Credo che la tua soluzione hacky funzionerebbe davvero. Assicurati solo di avere un'istruzione lock attorno ai bit in cui incrementa e decrementa il contatore:

class EmailSender
{
  object SimultaneousEmailsLock;
  int SimultaneousEmails;
  public string[] Recipients;

  void SendAll()
  {
    foreach(string Recipient in Recipients)
    {
      while (SimultaneousEmails>10) Thread.Sleep(10);
      SendAnEmail(Recipient);
    }
  }

  void SendAnEmail(string Recipient)
  {
    lock(SimultaneousEmailsLock)
    {
      SimultaneousEmails++;
    }

    ... send it ...
  }

  void FinishedEmailCallback()
  {
    lock(SimultaneousEmailsLock)
    {
      SimultaneousEmails--;
    }

    ... etc ...
  }
}

Vorrei aggiungere tutti i miei messaggi a una coda, quindi spawn cioè 10 thread che hanno inviato e-mail fino a quando la coda era vuota. C # pseudo'ish (probabilmente non compilerà):

class EmailSender
{
    Queue<Message> messages;
    List<Thread> threads;

    public Send(IEnumerable<Message> messages, int threads)
    {
        this.messages = new Queue<Message>(messages);
        this.threads = new List<Thread>();
        while(threads-- > 0)
            threads.Add(new Thread(SendMessages));

        threads.ForEach(t => t.Start());

        while(threads.Any(t => t.IsAlive))
            Thread.Sleep(50);
    }

    private SendMessages()
    {
        while(true)
        {
            Message m;
            lock(messages)
            {
                try
                {
                    m = messages.Dequeue();
                }
                catch(InvalidOperationException)
                {
                    // No more messages
                    return;
                }
            }

            // Send message in some way. Not in an async way, 
            // since we are already kind of async.

            Thread.Sleep(); // Perhaps take a quick rest
        }
    }
}

Se il messaggio è lo stesso e ha solo molti destinatari, basta scambiare il messaggio con un destinatario e aggiungere un singolo parametro Message al metodo Send.

È possibile utilizzare un timer .NET per impostare la pianificazione per l'invio di messaggi. Ogni volta che scatta il timer, prendi i prossimi 10 messaggi, inviali tutti e ripeti. Oppure, se si desidera un tasso generale (10 messaggi al secondo), è possibile attivare il timer ogni 100 ms e inviare un singolo messaggio ogni volta.

Se hai bisogno di una pianificazione più avanzata, puoi consultare un framework di pianificazione come Quartz.NET

Non è qualcosa che Thread.Sleep () può gestire?

Hai ragione nel pensare che il threading in background possa servire a un buon scopo qui. Fondamentalmente quello che vuoi fare è creare un thread in background per questo processo, lasciarlo eseguire a modo suo, ritardi e tutto il resto, quindi terminare il thread al termine del processo o lasciarlo acceso indefinitamente (trasformandolo in un servizio Windows o qualcosa di simile sarà una buona idea).

Un po 'di intro sul multi-threading può essere letto qui (con Thread.Sleep incluso! ) .

Una bella introduzione sui servizi di Windows può essere letta qui .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top