0

В моем приложении я использую ActionBlock из библиотеки Dataflow, чтобы отправить оповещения по электронной почте, используя SmtpClient.SendAsync() метод, который не блокирует вызывающий поток. (ActionBlock получают свои данные от BufferBlock, и блоки соединяются вместе, используя bufferBlock.LinkTo(actionBlock)). Однако этот метод будет вызывать InvalidOperationException, если выполняется другой вызов .SendAsync().Как безопасно использовать SmtpClient.SendAsync в применении многопоточного

В соответствии с MSDN documentation есть public event SendCompletedEventHandler SendCompleted, который поднимается при завершении операции отправки.

Как я могу убедиться, что гонка между нитями (или Tasks), порожденная ActionBlock, не вызовет поворота InvalidOperationException?

Одна мысль, до сих пор, заключается в том, чтобы добавить к моему классу (который отправляет электронные письма) частный замок вокруг SendAsync() вызов и приватная функция, которая будет назначена событию SendCompleted. Когда поток достигает SendAsync(), он получает блокировку, и когда событие поднимается, частная функция блокирует блокировку, позволяя другим потокам получать блокировку и прогресс.

ответ

1

Вместо этого вы должны просто использовать SendMailAsync. Он начнется с вызова SendAsync и будет завершен, когда будет поднят SendCompleted.

Для того чтобы быть уверенным, что только 1 сообщение отправлено, вы можете использовать SemaphoreSlim, установленный в 1. Это в основном AsyncLock.

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

var client = _smtpClientPool.Get(); 
try 
{ 
    await client.SendMailAsync(...) 
} 
finally 
{ 
    _smtpClientPool.Put(client); 
} 
3

Создайте один SmtpClient для каждой операции отправки. Таким образом, нет необходимости синхронизировать что-либо. Просто вставьте его в using для очистки.

Смежные вопросы