ThreadPool и отправка писем - PullRequest
       43

ThreadPool и отправка писем

2 голосов
/ 13 марта 2012

В настоящее время мы отправляем электронные письма пользователям асинхронно, используя ThreadPool.По сути, у нас есть логика, которая сводится к следующему:

for (int i=0 < i < numUsers; i++) 
{

   //Pre email processing unrelated to sending email

   string subject = GetSubject();
   string message = GetMessage();
   string emailAddress = GetEmailAddress();

   EmailObj emailObj = new EmailObj { subject = subject, message = message, emailAddress = emailAddress };

   bool sent = ThreadPool.QueueUserWorkItem(new WaitCallback(SendEmail), emailObj);

   //Post email processing unrelated to sending email
}

public void SendEmail(object emailObj)
{
    //Create SMTP client object
    SmtpClient client = new SmtpClient(Configuration.ConfigManager.SmtpServer);

    //Logic to set the subject, message etc
    client.Send(mail);
}

Логика до сих пор прекрасно работает с небольшим количеством пользователей.Мы пытаемся масштабировать это, чтобы иметь возможность отправлять около миллиона писем.

Для каждого MSDN максимальное количество потоков пула потоков основано на памяти, и в соответствии с этим SO ответом для 64-битной архитектуры получается, что максимальное число потоков пула потоков равно32768.

Означает ли это, что, если количество электронных писем, которые мы отправляем за раз, <32768, у нас все будет хорошо?Что происходит при превышении этого числа?Что произойдет, если служба SMTP будет отключена или будет задержана отправка электронного письма, будет ли поток пула потоков ждать отправки электронного письма? </p>

Когда число потоков превышает пороговое значение, отмечается ли в разделе // После обработки электронной почты, не связанной с отправкой электронной почты, вообще выполняется?

Любые объяснения действительно приветствуются.

Ответы [ 3 ]

3 голосов
/ 13 марта 2012

Потоки имеют накладные расходы - 1 МБ локального хранилища потоков. Вы никогда не захотите иметь 32K потоков в вашем пуле потоков. Пул потоков используется для управления и совместного использования потоков, поскольку они имеют служебные данные. Если пул потоков насыщается, будущие вызовы ставятся в очередь и ожидают доступного потока в пуле.

Еще одна вещь, которую следует учитывать, это то, что SMTP-серверы являются асинхронными (удаление в исходящей папке). Также, как кто-то упомянул выше, это может быть горлышко бутылки.

Одним из вариантов является увеличение пропускной способности за счет увеличения количества «агентов», отправляющих почту, и увеличения количества SMTP-серверов для масштабирования решения. Возможность независимо масштабировать агентов и SMTP-серверы позволяет устранить узкое место.

2 голосов
/ 13 марта 2012

Для 64-битной архитектуры максимальное число потоков пула потоков составляет 32768.

Не совсем, это максимальное количество потоков по умолчанию . Вы можете изменить это, позвонив ThreadPool.SetMaxThreads().

Значит ли это, что если количество отправляемых нами писем за раз <32768, у нас все будет хорошо? Что происходит при превышении этого числа? </p>

С вами все будет в порядке, даже если количество писем превышает этот порог. Весь смысл ThreadPool заключается в объединении потоков. Это означает, что он создает больше потоков только тогда, когда думает, что ваша производительность от этого выиграет. Маловероятно, что он создаст столько потоков, даже если вы попытаетесь отправить десятки тысяч писем за раз.

Когда ThreadPool думает, что вы не выиграете от создания другого потока, и вы добавите к нему больше работы, он будет поставлен в очередь и будет обработан, когда завершится какой-то другой поток, или когда ThreadPool передумает и создаст новая тема.

Итак, создание многих рабочих элементов безопасно, но может привести к другой проблеме: голоданию. Если вы создаете электронные письма быстрее, чем можете их отправить, у вас есть большие проблемы.

Что происходит, когда служба SMTP отключена?

Send() выдаст исключение. И поскольку кажется, что вы его не поймаете, это приведет к краху всего вашего приложения. То же самое произойдет, если письмо не может быть отправлено по другой причине.

Что произойдет, если при отправке электронной почты будет задержка, будет ли поток пула потоков ждать отправки сообщения?

Да, пока электронная почта отправляется на сервер, поток блокируется. ThreadPool может обнаружить это и, вероятно, создаст другой поток, если какое-то другое электронное письмо ожидает отправки. Но это, вероятно, не то, что вы хотите, это, вероятно, сделает сервер еще медленнее.

Чтобы помочь с этим, вы можете ограничить максимальное количество ThreadPool потоков. Но это глобальная настройка для вашего приложения. Вероятно, было бы лучше, если бы вы использовали Task s с пользовательским TaskSheduler, что позволит вам ограничить число отправляемых одновременно электронных писем, но не ограничит другую работу, которая может происходить на ThreadPool , Это особенно важно для приложений ASP.NET, которые используют ThreadPool для обработки запросов, но в любом случае, вероятно, не разрешат изменять количество потоков в этом случае.

2 голосов
/ 13 марта 2012

Использование методов пула потоков является правильным решением здесь.Хотя, если это возможно, я бы использовал класс Task, если он вам доступен, но ThreadPool.QueueUserWorkItem также является хорошим маршрутом.Я сомневаюсь, что ThreadPool фактически создаст 32768 потоков в реальности, хотя это может быть теоретическим максимумом.Потоки - не дешевый ресурс, поэтому он сводит фактическое количество к минимуму.Но это не имеет значения, сколько реальных потоков существует.Все рабочие элементы помещаются в очередь все равно.Даже если есть только один поток, обрабатывающий очередь, он в конечном итоге будет очищен.

Миллион - это довольно много писем.В зависимости от размера структуры данных, в которой хранятся данные вашей электронной почты, у вас могут возникнуть проблемы с памятью, если вы попытаетесь поставить их всех в очередь сразу.Вы могли бы реализовать какую-то стратегию регулирования, чтобы поддерживать количество живых объектов на низком уровне и, следовательно, нагрузку на память в пределах нормы.Семафор был бы полезным инструментом, чтобы помочь вам задушить вещи.

var semaphore = new SemaphoreSlim(10000, 10000); // Allow 10000 work items at a time
for (int i=0 < i < numUsers; i++)  
{ 
   semaphore.Wait(); // Blocks if there are too many pending work items
   string subject = GetSubject(); 
   string message = GetMessage(); 
   string emailAddress = GetEmailAddress(); 
   EmailObj emailObj = new EmailObj { subject = subject, message = message, emailAddress = emailAddress };  
   bool sent = ThreadPool.QueueUserWorkItem(
      (state) =>
      {
        try
        {
          SendEmail(emailObj);
        }
        finally
        {
          semaphore.Release(); // This work item is done.
        }
      }, null); 
} 
...