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

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

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

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

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

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

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

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

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

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