Invalid Operation-Ausnahme beim Aufrufen von Asynchronen Methode von innen eine Parallele.ForEach-Schleife
Ich geerbt habe einen windows-Dienst, der Prozesse eine große Anzahl von e-mails in einer Warteschlange. Klingt einfach, Greifen Warteschlange, senden Sie eine e-mail, wenn SmtpClient.SendAsync nicht wieder ein Fehler aus dem Aufruf zurück, dann markieren Sie die e-mail in die DB gesendet wird.. ich bin mit einem Semaphor zu waitone auf den Faden, so dass mehrere Anrufe getätigt werden können, um die Async-Senden-Methode des SMTP-Client. Dies ist der einzige Weg, bekomme ich den status und pro Microsoft docs hat, um die operation abzuschließen, bevor ein weiterer Anruf kann getätigt werden async. So, jetzt zum spaßigen Teil. Ich habe mich entschieden, eine Parallele.ForEach zu bekommen, er Warteschlange so gerne. Diese Methode wird als der Windows-Dienst OnStart. Bitte beachten Sie, ich habe versucht, den Aufruf dieser Methode in einen separaten Thread und die gleichen Ergebnisse erhalten.
Denke ich, dass Sie entweder Ein, ich bin etwas fehlt, offensichtlich, wegen meiner mangelnden Kenntnisse auf dem Durchzug, oder etwas flach ist verbuggt. Wahrscheinlich 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
});
}
Hier ist die SMTP-Methode aufgerufen wird, von innerhalb der Schleife.
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;
}
}
RÜCKRUF Für Smtp Senden
private void smtp_SendCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled)
{
}
if (e.Error != null)
{
}
else
{
_mailSent = true;
}
mailSendSemaphore.Release(2);
}
Hier ist die Ausnahme, nahm ein paar, um es für einige ungerade Grund.
System.InvalidOperationException was unhandled by user code
Message=Ein asynchroner Aufruf ist bereits im Gange. Es muss abgeschlossen oder abgebrochen, bevor Sie diese Methode aufrufen kann.
Source=System
StackTrace:
System.Net.E-Mail.SmtpClient.SendAsync(MailMessage message, Object userToken)
am Dallas-Fort Worth.Infrastruktur.Kommunikation.SmtpMail.SendMessage() in SmtpMail.cs:Zeile 71
bei EmaiProcessorService.EmailQueueService.b_0(EmailQueue Nachricht) in "Service1".cs:Zeile 57
System.Threading.Aufgaben.Parallel.<>c_DisplayClass2d2.<ForEachWorker>b__23(Int32 i)
1.b__c()
at System.Threading.Tasks.Parallel.<>c__DisplayClassf
InnerException:
Scheint meine waitone immer ausgelöscht, die durch das System.Threading.Aufgaben.Parallel
- Wie immer, bitte geben Sie die details der InvalidOperationException - die Zeile wirft eine exception, was ist die Botschaft, etc?
- Versucht, es zu fangen, wie wir sprechen, es scheint etwas versteckt, wie ich kann nicht scheinen, um es in meiner try-catch-Blöcke
- Debuggen von Windows-Diensten Ist nicht sehr unterhaltsam 🙂
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich bin mir nicht klar, warum Sie verwenden einen Semaphor-hier, aber du bist fast selbstverständlich ist es falsch. Beim erstellen eines neuen semaphore-Instanz für jeden Aufruf
SendMessage
. Auch Sie fordernWaitOne
mal drauf, und dann ruftRelease(2)
, so schließlich haben Sie mehr releases als erwirbt. Das ist wahrscheinlich das, was bewirkt, dass IhrInvalidOperationException
.Es nicht tun Sie etwas gutes zu parallelisieren Verarbeitung der E-Mail-Warteschlange, da kann man nur senden eine Nachricht zu einem Zeitpunkt. Und versuchen, es zu tun asynchron innerhalb der
Parallel.Foreach
ist nur mehr eine unnötige Komplikation.Du bist besser dran mit so etwas wie
ThreadPool.QueueUserWorkItem
, und mit einer einfachen Schleife, die sendet eine Nachricht gleichzeitig.Alternativ, Sie können das gleiche tun mit einem
Task
. Der Punkt ist, dass Sie brauchen nur einen einzigen thread zum verarbeiten der Warteschlange der Reihe nach ab. Da kann man nicht senden mehr als eine Nachricht gleichzeitig,Parallel.ForEach
tut dir nicht gut.EDIT:
Wenn Sie brauchen, um mehrere sends zu einem Zeitpunkt,, Sie können sich wahrscheinlich ändern Sie Ihre original-code. Zunächst initialisiert die semaphore-Klasse Umfang:
Dann in Ihrer
SendMessage
Methode:Gibt es keine Notwendigkeit zu verwenden
SendAsync
.BackgroundWorker
Feuer und das Progress-Ereignis wird für jede E-mail gesendet. Wenn SieThreadPool.QueueUserWorkItem
haben, können Sie es nennen eine definierte callback-mit dem status.Okay, jetzt haben wir den Fehler text, es scheint ziemlich klar:
Dies deckt sich mit der Dokumentation:
Zwei einfache Optionen:
Erstellen Sie eine Feste Anzahl von clients, und eine Warteschlange mit Nachrichten zu senden. Machen Sie jeden client eine Nachricht aus der Warteschlange jedes mal, wenn es fertig ist, bis die Warteschlange leer ist.
BlockingCollection<T>
ist gut für diese.Erstellen Sie eine neue
SmtpClient
pro Nachricht. Dies könnte dazu führen, Sie effektiv zu starten Sie eine DOS-Attacke auf Ihrem SMTP-server, das ist nicht ideal.Um ehrlich zu sein, es ist nicht wirklich klar, warum man mit
SendAsync
wenn man dann nur darauf warten, für den die Nachricht gesendet werden sowieso...