如何在多线程应用程序中安全地使用SmtpClient.SendAsync

时间:2015-02-20 18:52:14

标签: c# .net multithreading task-parallel-library tpl-dataflow

在我的应用程序中,我使用来自Dataflow库的ActionBlock,使用SmtpClient.SendAsync()方法发送电子邮件警报,该方法不会阻止调用线程。(ActionBlock从中获取数据BufferBlock,并使用bufferBlock.LinkTo(actionBlock)将块绑定在一起。但是,如果正在进行另一个InvalidOperationException调用,此方法将抛出.SendAsync()

根据MSDN documentation,发送操作完成时会引发public event SendCompletedEventHandler SendCompleted

如何确保Tasks产生的线程(或ActionBlock)之间的竞争不会导致InvalidOperationException被抛出?

到目前为止,有一个想法是将我的课程(发送电子邮件)添加到SendAsync()调用的私有锁定以及将分配给SendCompleted事件的私有函数。当线程到达SendAsync()时,它获得锁定,并且当引发事件时,私有函数解锁锁定,允许其他线程获得锁定和进度。

2 个答案:

答案 0 :(得分:3)

为每个发送操作创建一个SmtpClient。这样就不需要同步任何东西了。只需将其括在using中即可清理。

答案 1 :(得分:2)

您应该只使用SendMailAsync。它将从调用SendAsync开始,并在SendCompleted被提升时完成。

要确保每次只发送一条消息,您可以将SemaphoreSlim设置为1.它基本上是AsyncLock

但是,这会限制并行性,因为您一次只能执行一个操作。您可以简单地使用许多不同的客户。如果每个调用不是一个,那么使用一些资源池,然后你可以有并发性,但仍然保持每个客户端线程安全。

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