在我的应用程序中,我使用来自Dataflow库的ActionBlock
,使用SmtpClient.SendAsync()
方法发送电子邮件警报,该方法不会阻止调用线程。(ActionBlock
从中获取数据BufferBlock
,并使用bufferBlock.LinkTo(actionBlock)
将块绑定在一起。但是,如果正在进行另一个InvalidOperationException
调用,此方法将抛出.SendAsync()
。
根据MSDN documentation,发送操作完成时会引发public event SendCompletedEventHandler SendCompleted
。
如何确保Tasks
产生的线程(或ActionBlock
)之间的竞争不会导致InvalidOperationException
被抛出?
到目前为止,有一个想法是将我的课程(发送电子邮件)添加到SendAsync()
调用的私有锁定以及将分配给SendCompleted
事件的私有函数。当线程到达SendAsync()
时,它获得锁定,并且当引发事件时,私有函数解锁锁定,允许其他线程获得锁定和进度。
答案 0 :(得分:3)
为每个发送操作创建一个SmtpClient
。这样就不需要同步任何东西了。只需将其括在using
中即可清理。
答案 1 :(得分:2)
您应该只使用SendMailAsync
。它将从调用SendAsync
开始,并在SendCompleted
被提升时完成。
要确保每次只发送一条消息,您可以将SemaphoreSlim
设置为1.它基本上是AsyncLock
。
但是,这会限制并行性,因为您一次只能执行一个操作。您可以简单地使用许多不同的客户。如果每个调用不是一个,那么使用一些资源池,然后你可以有并发性,但仍然保持每个客户端线程安全。
var client = _smtpClientPool.Get();
try
{
await client.SendMailAsync(...)
}
finally
{
_smtpClientPool.Put(client);
}