6
votes

Envoi d'e-mails sur des threads séparés à l'aide de WeeueuserworkItem

J'ai une application de console qui envoie des courriels personnalisés (avec des pièces jointes) à différents destinataires et je souhaite les envoyer simultanément. J'ai besoin de créer des smtterclients distincts pour y parvenir, de sorte que j'utilise de la queueUserWorkitem pour créer les courriels et les envoyer dans des threads distincts.

Snippet B> P>

SmtpClient client = new SmtpClient("Host");
FieldInfo transport = client.GetType().GetField("transport", BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo authModules = transport.GetValue(client).GetType()
    .GetField("authenticationModules", BindingFlags.NonPublic | BindingFlags.Instance);
Array modulesArray = authModules.GetValue(transport.GetValue(client)) as Array;
modulesArray.SetValue(modulesArray.GetValue(2), 0);
modulesArray.SetValue(modulesArray.GetValue(2), 1);
modulesArray.SetValue(modulesArray.GetValue(2), 3);
try
{
    // create mail message
    ...
    emailClient.Send(emailAlert);
}
catch (Exception ex)
{
    // log exception
}
finally
{
    emailAlert.Dispose();
}


7 commentaires

Pouvez-vous créer un programme court, mais complet, qui présente le problème?


@Lasse je vais essayer les solutions suggérées puis poster si encore rien.


Pourquoi ne pouvez-vous pas utiliser sendasync et simplement traiter les événements d'achèvement afin que vous sachiez si tous les courriels ont été envoyés?


Je pense que vous avez manqué une partie du code. Je ne vois aucun appel à envoyer.


@Brian je veux envoyer les alertes dès que possible et ils peuvent être déclenchés à peu près 1 après l'autre, vous n'êtes autorisé à envoyer que 1 de manière asychrone à 1 heure, c'était mon idée originale.


@Kiquenet vous n'attendez pas sérieusement que je publie mon code d'application complet, vous? Le code affiché dans ma question et les réponses ci-dessous vous suffit-il de continuer?


Application non complète, seulement envoi de processus de messagerie s'il est possible comme celui-ci Stackoverflow.com/a/1687178/206730 Je voudrais comparer échantillons, pour trouver plus clair, plus élégant code


4 Réponses :


2
votes

Vous voulez probablement faire cela ...

var events = new Dictionary<Guid, AutoResetEvent>();
foreach (...)
{
    var id = Guid.NewGuid();
    events.Add(id, new AutoResetEvent(false));
    ThreadPool.QueueUserWorkItem((state) =>
    {           
        // Send Email
        events[(Guid)state].Set();
    }, id);   
}
// wait for all emails to signal
WaitHandle.WaitAll(events.Values.ToArray());


9 commentaires

ou threadpool.QueueUeueraRorworkItem ((état) => {Événements [((GUID) état] .set ();}, id);


Je l'ai fait, donc je n'ai pas à demander s'il a .net 3.5.


Pour le record que j'ai 3,5, essayez juste votre solution à la MO vous recontactera les gars!


Hmm, le var aurait dû être un drapeau rouge pour moi. J'ai besoin de vacances.


Même résultat, 1 e-mail Cette heure n'est pas arrivée dans la boîte aux lettres (j'ai attendu un peu plus pour cela), puis ajoutez le sommeil du fil travaillé!


Cassez-le pour moi, travaillé ou n'a pas fonctionné .


En fait, votre version est probablement un peu meilleure que la mienne, mais si vous allez passer l'identifiant comme Etat, pourquoi ne pas transmettre le autoréfortezevent à la place? Cela éliminerait la recherche de dictionnaire potentiellement non threadsafe. Ou mieux encore, ne laissez pas l'état du tout; Stockez le AutoSetevent dans une variable locale avant de l'ajouter au dictionnaire et simplement mettre event.set () dans le délégué. Plus je le regarde, plus le dictionnaire semble inutile, vous pouvez le faire tout aussi avec une liste .


Depuis que la recherche est en lecture seule, nous pouvons supposer que c'est le fil sûr. Je pense que le problème est que le programme se termine trop rapidement et tue l'envoi des emails.


@Chaos, c'est le problème, j'essaie d'arrêter cela de se produire!



4
votes

Une des choses qui me dérangent de votre code est que vous appelez events.add dans la méthode du thread. Le dictionnaire n'est pas thread-coffre-fort; Ce code ne doit pas être à l'intérieur du fil.

Mise à jour: Je pense que la chaospandion a affiché une bonne implémentation, mais je le ferais encore plus simple, ce qui ne le fait que rien ne peut éventuellement Allez mal en termes de sécurité-sécurité: xxx

J'ai éliminé complètement le dictionnaire ici, et tous les instances autoréforme sont créées dans le même fil qui effectue plus tard un waitall . Si ce code ne fonctionne pas, cela doit être un problème avec l'e-mail lui-même; Soit le serveur est d'abandonner les messages (combien d'envois êtes-vous envoyé?) Ou vous essayez de partager quelque chose de non-fil-sûr-en sécurité entre alerte des instances (éventuellement un singleton ou quelque chose déclaré statique).


6 commentaires

Mais sûrement chaque fil est signalé correctement, sinon mon application attendrait pour toujours?


Pas si le watiser arrive avant que tous les threads ont même eu la chance d'ajouter leur événement au dictionnaire. C'est pourquoi il est important d'initialiser la liste dans le même fil que vous avez attendu.


Ah très bon point! Je pense que vous aurez peut-être frappé le clou sur la tête.


Avez-vous essayé la version mise à jour? J'ai l'impression que l'accès du dictionnaire peut avoir fait partie de la question, ce n'est pas un fil-coffre-fort. Construisez la liste des événements dans le fil avant-plan, éloignez-vous complètement le dictionnaire, et vous devriez aller bien.


Tache sur, cela semblait résoudre le problème. Merci pour votre temps.


Une utilisation génie des fermetures, une chose que vous pourriez faire est de raccourcir délégué à o => .



2
votes

La raison pour laquelle il ne fonctionne pas, c'est que lorsqu'il frappe des événements.values.Taray () Tous les délégués en file d'attente n'ont pas exécuté em> et donc Tous les cas possible n'ont pas été ajoutés à la Dictionnaire em>.

Lorsque vous appelez TOARRAY () sur la propriété Valeurs, vous obtenez uniquement ceux-ci sont des instances déjà ajoutées! p>

Cela signifie que vous n'attendrez que quelques-uns des courriels à envoyer de manière synchrone avant la Le fil bloqué continue. Le reste des courriels n'a pas encore été traité par les threads threadpool. P>

Il y a une meilleure façon, mais c'est un hack frappe> il semble inutile de faire quelque chose d'asynchrone quand vous voulez Pour bloquer le fil d'appel à la fin ... p>

var doneLol = new AutoResetEvent();

ThreadPool.QueueUserWorkItem(
delegate
{
  foreach (...)
  {
    var id = Guid.NewGuid();
    var alert = HurrDurr.CreateAlert(...);
    alert.Send();
  }
  doneLol.Set();
});   

doneLol.WaitOne();


13 commentaires

Bien sûr, cela n'utilise qu'un fil pour toutes les alertes ... ils sont toujours envoyés séquentiellement au lieu de parallèlement. Si l'objectif est juste de le faire à l'arrière-plan, cela fonctionnera bien, mais s'il essaie de les envoyer tous en parallèle, cela ne fait pas exactement la même chose.


Je sais que c'est à peu près inutile. Pourquoi cette asynchrone quand vous voulez bloquer?


J'ai supposé que c'était afin d'améliorer le débit global. Envoyer () Peut prendre une période relativement longue pour exécuter, mais si le serveur de messagerie accepte 10 connexions en même temps, la version parallèle finira beaucoup plus vite.


Je veux que les alertes vont dès que possible et que je veux juste que l'application attendait que toutes les alertes aient été envoyées. Si l'application se termine trop rapidement, certaines alertes ne sont pas envoyées, pourquoi j'ai besoin d'attendre.


Eh bien, si alerter.send.send () bloque jusqu'à l'envoi (vous devrez créer un projet de démonstration pour tester cela), puis envoyez-les de manière synchrone. Si vous souhaitez empêcher l'arrêt de l'application de les arrêter d'être envoyé, créez un nouveau thread et utilisez-le pour exécuter vos alertes (c'est-à-dire que vous n'utilisez pas de thread "de fond"; voir ici: msdn.microsoft.com/en-us/library/h339syd0.aspx )


@Will ... C'est ce que j'ai fait, regarde mon message d'origine. J'envoie les courriels dans des threads séparés.


@Will: C'est exactement ça. La méthode envoyer bloque probablement une seconde ou deux, mais la majeure partie de cette période est inactive, il s'agit simplement d'envoyer des données sur le fil et / ou d'attendre une réponse à partir d'un serveur de messagerie. Les envoyer en parallèle améliorera le débit (je suppose).


Honnêtement, je sauverais simplement la multithreading lorsque vos exigences ne semblent pas appeler cela. Si vous vous retrouvez avec une métrique S * tton d'emails, le threadpool ne sera pas beaucoup plus efficace que la fonctionner de manière synchrone (en fonction du nombre de cœurs et de quoi d'autre se passe avec la boîte).


Sinon, vous pouvez créer x nombre de threads (peut-être 2x cœurs CPU) et diviser les courriels entre eux. Je ferais probablement cela en utilisant un concurrentqueur Holding Actions que chaque thread exécute ensuite ...


@Will Pour le moment, il n'y aura pas vraiment de nombreux courriels allant, cependant, à l'avenir, je m'attends à un certain nombre. Je vais toujours examiner la solution Concurrentqueure Merci.


C'est une collection 4.0, BTW.


Ah je suis sur 3,5, y a-t-il quelque chose que je pouvais regarder dans cette version?


Hmmm ... Il est originaire de la bibliothèque de FX parallèle, mais qui n'a jamais eu de licence Go-Live. Vous voudrez peut-être examiner la bibliothèque de filetage de l'alimentation de Jeffrey Richter (I.e., Wintellect). Je crois que cela a des collections simultanées.



0
votes

J'ai eu un problème similaire (en utilisant smtpclient à partir d'un thread et que les courriels n'arrivent que par intermittence).

Initialement la classe qui envoie des courriels a créé une seule instance de SmtpClient. Le problème a été résolu en changeant le code pour créer une nouvelle instance de SMTPCLIPT chaque fois qu'un email doit être envoyé et strong> disposer du SMTPCLIPT (avec une instruction utilisant). P>

 SmtpClient smtpClient = InitSMTPClient();
 using (smtpClient)
 {
    MailMessage mail = new MailMessage();
    ...
    smtpClient.Send(mail);
 }


0 commentaires