使用QueueUserWorkItem在单独的线程上发送电子邮件

Jam*_*mes 6 c# multithreading smtp

我有一个控制台应用程序,可以向不同的收件人发送自定义的电子邮件(带附件),我想同时发送它们.我需要创建单独的SmtpClients来实现这一点,所以我使用QueueUserWorkItem来创建电子邮件并将它们发送到不同的线程中.

片段

var events = new Dictionary<Guid, AutoResetEvent>();
foreach (...)
{
    ThreadPool.QueueUserWorkItem(delegate
    {
        var id = Guid.NewGuid();
        events.Add(id, new AutoResetEvent(false));
        var alert = // create custom class which internally creates SmtpClient & Mail Message
        alert.Send();
        events[id].Set();
    });   
}
// wait for all emails to signal
WaitHandle.WaitAll(events.Values.ToArray());
Run Code Online (Sandbox Code Playgroud)

我已经注意到(间歇性地)有时并非所有电子邮件都使用上述代码到达特定邮箱.我原以为使用Sendover SendAsync意味着电子邮件肯定是从应用程序发送的.但是,在行之后添加以下代码WaitHandle.WaitAll行:

System.Threading.Thread.Sleep(5000);
Run Code Online (Sandbox Code Playgroud)

似乎工作.我的想法是,无论出于何种原因,仍然没有发送一些电子邮件(即使在Send方法运行之后).给予额外的5秒似乎给应用程序足够的时间来完成.

这可能是我等待发送电子邮件的方式的问题吗?或者这是实际Send方法的问题?一旦我们通过这条线,电子邮件肯定是从应用程序发送的吗?

任何有关此问题的想法都会很棒,似乎无法完全理解实际原因.

更新

这里要求的是SMTP代码:

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();
}
Run Code Online (Sandbox Code Playgroud)

Aar*_*ght 4

你的代码让我烦恼的事情之一是你events.Add在线程方法中调用。该类Dictionary<TKey, TValue>不是线程安全的;这段代码不应该在线程内部。

更新:我认为 ChaosPandion 发布了一个很好的实现,但我会让它变得更简单,这样在线程安全方面就不会出错

var events = new List<AutoResetEvent>();
foreach (...)
{
    var evt = new AutoResetEvent();
    events.Add(evt);
    var alert = CreateAlert(...);
    ThreadPool.QueueUserWorkItem(delegate
    {           
        alert.Send();
        evt.Set();
    });
}
// wait for all emails to signal
WaitHandle.WaitAll(events.ToArray());
Run Code Online (Sandbox Code Playgroud)

我在这里完全消除了字典,并且所有实例AutoResetEvent都是在稍后执行WaitAll. 如果此代码不起作用,则一定是电子邮件本身有问题;服务器正在丢弃消息(您发送了多少条消息?),或者您试图在实例之间共享非线程安全的内容Alert(可能是单例或静态声明的内容)。