加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-15 08:19:40 所属栏目:百科 来源:网络整理
导读:我有一个控制台应用程序,可以向不同的收件人发送自定义的电子邮件(带附件),我想同时发送它们.我需要创建单独的SmtpClients来实现这一点,所以我使用QueueUserWorkItem来创建电子邮件并将它们发送到不同的线程中. 片段 var events = new DictionaryGuid,AutoRe
我有一个控制台应用程序,可以向不同的收件人发送自定义的电子邮件(带附件),我想同时发送它们.我需要创建单独的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实例之间共享一些非线程安全的东西(可能是单例或静态声明的东西).

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读