gpt4 book ai didi

c# - 从 Parallel.ForEach 循环内部调用异步方法时出现无效操作异常

转载 作者:行者123 更新时间:2023-11-30 22:35:26 27 4
gpt4 key购买 nike

我继承了一个处理队列中大量电子邮件的windows服务。听起来很简单,抓取队列,发送电子邮件,如果 SmtpClient.SendAsync 没有从回调中返回错误,然后将数据库中的电子邮件标记为已发送。我正在使用信号量在线程上等待多个可以调用 SMTP 客户端的异步发送方法。这是我可以获得状态的唯一方法,并且根据 Microsoft 文档,它必须在另一个调用可以异步之前完成操作。所以现在是有趣的部分。我决定使用 Parallel.ForEach 来让他像这样排队。此方法在 Windows 服务 OnStart 中调用。请注意,我曾尝试在单独的线程上调用此方法并获得相同的结果。

我在想,要么 A,由于我缺乏线程知识,我遗漏了一些明显的东西,或者某些东西被窃听了。很可能是 A。

 private static void ProcessEmailQueue()
{
List<EmailQueue> emailQueue =
_repository.Select<EmailQueue>().Where(x => x.EmailStatuses.EmailStatus == "Pending").ToList();
Parallel.ForEach(emailQueue, message =>
{
_smtpMail.FromAddress = message.FromAddress;
_smtpMail.ToAddress = message.ToAddress;
_smtpMail.Subject = message.Subject;
_smtpMail.SendAsHtml = message.IsHtml > 0;
_smtpMail.MessageBody = message.MessageBody;
_smtpMail.UserToken = message.EmailQueueID;
bool sendStatus = _smtpMail.SendMessage();
// THIS BLOWS UP with InvalidOperation Exception
});
}

这是从循环中调用的 SMTP 方法。

public bool SendMessage()
{
mailSendSemaphore = new Semaphore(0, 10); // This is defined as private static Semaphore mailSendSemaphore;
try
{
var fromAddress = new MailAddress(FromAddress);
var toAddress = new MailAddress(ToAddress);

using (var mailMessage = new MailMessage(fromAddress, toAddress))
{
mailMessage.Subject = Subject;
mailMessage.IsBodyHtml = SendAsHtml;
mailMessage.Body = MessageBody;
Envelope = mailMessage;
smtp.SendCompleted += smtp_SendCompleted;
smtp.SendAsync(mailMessage, UserToken);
mailSendSemaphore.WaitOne();
return _mailSent;
}
}
catch (Exception exception)
{
_logger.Error(exception);

return _mailSent;
}
}

用于 Smtp 发送的回调

 private void smtp_SendCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled)
{
}
if (e.Error != null)
{
}
else
{
_mailSent = true;
}
mailSendSemaphore.Release(2);
}

这是异常,由于一些奇怪的原因花了一些时间才得到它。

System.InvalidOperationException was unhandled by user code

Message=一个异步调用已经在进行中。必须先完成或取消,然后才能调用此方法。 源=系统 堆栈跟踪: 在 System.Net.Mail.SmtpClient.SendAsync(MailMessage 消息,对象 userToken) 在 SmtpMail.cs 中的 DFW.Infrastructure.Communications.SmtpMail.SendMessage():第 71 行 在 EmaiProcessorService.EmailQueueService.b_0(EmailQueue 消息)在 Service1.cs 中:第 57 行 在 System.Threading.Tasks.Parallel.<>c_DisplayClass2d 2.<ForEachWorker>b__23(Int32 i)
at System.Threading.Tasks.Parallel.<>c__DisplayClassf
1.b__c() 内部异常:

似乎我的 waitone 被 System.Threading.Tasks.Parallel 抹杀了

最佳答案

好的,现在我们已经有了错误文本,看起来很清楚了:

Message=An asynchronous call is already in progress. It must be completed or canceled before you can call this method.

这与 documentation 一致:

两个简单的选项:

  • 创建固定数量的客户端和要发送的消息队列。让每个客户端在每次完成时从队列中取出一条消息,直到队列为空。 BlockingCollection<T> 对此有好处。

  • 创建一个新的 SmtpClient 每条消息。这可能会导致您对 SMTP 服务器有效地发起 DOS 攻击,这并不理想。

老实说,您不太清楚为什么要使用 SendAsync当您只是等待消息被发送时...

关于c# - 从 Parallel.ForEach 循环内部调用异步方法时出现无效操作异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7434073/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com