c# – 使用QueueUserWorkItem在不同的线程上发送电子邮件

我有一个控制台应用程序,可以向不同的收件人发送自定义的电子邮件(带附件),我想同时发送它们.我需要创建单独的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());

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

System.Threading.Thread.Sleep(5000);

似乎工作.我的想法是,无论出于何种原因,仍然没有发送一些电子邮件(即使在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();
}

解决方法

令你烦恼的一件事就是你在线程方法调用events.Add.字典< TKey,TValue> class不是线程安全的;这段代码不应该在线程内.

更新:我认为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());

在这里完全删除了字典,并且所有AutoResetEvent实例都是在稍后执行WaitAll的同一个线程中创建的.如果此代码不起作用,那么它必须是电子邮件本身的问题;服务器是丢弃消息(你发送了多少?)或者你试图在Alert实例之间共享一些非线程安全的东西(可能是单例或静态声明的东西).

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...