Pergunta

Desculpe o título é um pouco ruim, eu não conseguia palavra-lo corretamente.

Edit: Devo observar este é um aplicativo de console c #

Eu tenho um protótipo um sistema que funciona como assim (este é áspera pseudo-codeish):

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();    
}

Obviamente, esta configuração não é ideal. Se a coleção inicial tem, digamos, 10.000 registros, em seguida, ele vai nova-se 10.000 casos de SmtpClient de forma bastante curto tão rapidamente como ele pode percorrer as linhas -. E provável Asplode no processo

Meu jogo final ideal é ter algo como 10 email concorrente sair ao mesmo tempo.

Uma solução hacky vem à mente: Adicionar um contador, que incrementos quando SendAnEmail () é chamado, e diminui quando o SendComplete é enviado. Antes SendAnEmail () é chamado no circuito inicial, verificar o contador, se for muito alto, então o sono por um pequeno período de tempo e, em seguida, verificá-lo novamente.

Eu não tenho certeza que é uma grande idéia, e figura a mente SO colméia teria uma maneira de fazer isso corretamente.

Eu tenho muito pouco conhecimento de threading e não tenho certeza se seria um uso apropriado aqui. Por exemplo, o envio de e-mail em uma discussão de fundo, verifique primeiro o número de threads de criança para garantir que não é demais ser usado. Ou se houver algum tipo de 'estrangulamento tópico' construído em.


Atualização

Seguindo o conselho de Steven A. Lowe, agora tenho:

  • Um dicionário segurando meus e-mails e uma chave única (este é o e-mail that
  • Um Método FillQue, que preenche o dicionário
  • Método A ProcessQue, que é uma discussão de fundo. Ele verifica o that, e SendAsycs qualquer e-mail em que.
  • Um delegado SendCompleted que remove o e-mail do que. E chama FillQue novamente.

Eu tenho alguns problemas com esta configuração. Eu acho que eu perdi o barco com a discussão de fundo, eu deveria estar gerando um destes para cada item no dicionário? Como posso obter a linha para 'hang around' por falta de uma palavra melhor, se esvazia Email que as extremidades da linha.


atualização final

Eu coloquei um 'while (true) {}' na discussão de fundo. Se o that está vazio, ele espera alguns segundos e tenta novamente. Se o that é repetidamente esvaziar, i 'break' ao mesmo tempo, e as extremidades programa ... funciona bem. Estou um pouco preocupado com o negócio 'while (true)' embora ..

Foi útil?

Solução

Resposta Curta

O uso de uma fila como um tampão finito, processado por sua própria rosca.

Long Resposta

Chamar um método de preenchimento-fila para criar uma fila de e-mails, limitado a (digamos) 10. preenchê-lo com os primeiros 10 e-mails não enviados. Lançar um thread para processar a fila - para cada e-mail na fila, enviá-lo asynch. Quando a fila está vazia sono por um tempo e de verificação de novo. Já o delegado conclusão remover o enviado ou e-mail errored da fila e atualizar o banco de dados, em seguida, chamar o método de preenchimento-fila para ler e-mails mais não enviadas para a fila (back até o limite).

Você só vai precisar de bloqueios em torno das operações de fila, e só terá de gerir (diretamente) a um segmento para processar a fila. Você nunca vai ter mais de N + 1 tópicos ativos ao mesmo tempo, onde N é o limite da fila.

Outras dicas

Eu acredito que a sua solução hacky realmente iria funcionar. Apenas certifique-se você tem uma declaração de bloqueio em torno dos bits onde você aumentar e diminuir o contador:

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 ...
  }
}

Gostaria de acrescentar todas as minhas mensagens para uma fila, e em seguida, desova ou seja, 10 tópicos que enviou e-mails até que a fila estava vazia. Pseudo'ish C # (provavelmente não compile):

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 a mensagem é a mesma, e apenas ter muitos destinatários, apenas trocar a mensagem com um destinatário, e adicionar um único parâmetro Mensagem para o método de envio.

Você pode usar um temporizador .NET para configurar a programação para o envio de mensagens. Sempre que os fogos temporizador, pegar os próximos 10 mensagens e enviá-los todos, e repita. Ou se você quiser uma taxa geral (10 mensagens por segundo) você poderia ter o fogo temporizador a cada 100ms, e enviar uma única mensagem de cada vez.

Se precisar de programação mais avançado, você pode olhar para um quadro de programação como Quartz.NET

Não é este algo que Thread.Sleep() pode segurar?

Você está correto em pensar que enfiar fundo pode servir um bom propósito aqui. Basicamente o que você quer fazer é criar uma discussão de fundo para este processo, deixá-lo correr o seu próprio caminho, atrasos e tudo, e, em seguida, encerrar o segmento quando o processo é feito, ou deixá-lo indefinidamente (transformando-o em um serviço do Windows ou algo similar será uma boa idéia).

Um pouco introdução no pode ser lido aqui (com Thread.Sleep incluído multi-threading! ) .

Um bom introdução no Windows Services pode ser lido aqui .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top