Multithreading 如何确保电子邮件队列处理线程之间的唯一本地状态

Multithreading 如何确保电子邮件队列处理线程之间的唯一本地状态,multithreading,c#-4.0,.net-4.0,Multithreading,C# 4.0,.net 4.0,场景: int itemCount = serMailObj.ReceipientList.Count; Parallel.For(0, itemCount, () => { return new ThreadLocalStateCache() { Recei

场景:

int itemCount = serMailObj.ReceipientList.Count;

                    Parallel.For(0, itemCount, () =>
                    {
                        return new ThreadLocalStateCache()
                        {
                            Receipient = serMailObj.ReceipientList.Dequeue(),
                            mail = serMailObj.Email,
                            SerializableSmtpDetails = serSmtpObj
                        };
                    }
     , doWork, (x) => { });

 private static ThreadLocalStateCache doWork(int instance, ParallelLoopState state, ThreadLocalStateCache threadInstance)
        {
            KeyValuePair<string, string> kvp = threadInstance.Receipient;
            SerializableSmtpDetails serSmtpObj = threadInstance.SerializableSmtpDetails;
            MailMessage email = threadInstance.mail;

                email.To.Add(new MailAddress(kvp.Key, kvp.Value));

                SmtpClient client = new SmtpClient();
                client.Credentials = new System.Net.NetworkCredential(serSmtpObj.UserName, serSmtpObj.Password);
                client.Host = serSmtpObj.Host;
                client.Port = serSmtpObj.Port;
                client.EnableSsl = serSmtpObj.EnableSSL;

                    try 
                    {           
                        client.Send(email);
                        Console.WriteLine("sending mail....");
                    }
                    catch (Exception)
                    {

                        throw;
                    }


            return null;
        }

public class ThreadLocalStateCache
    {
        public KeyValuePair<string, string> Receipient { get; set; }

        public MailMessage mail { get; set; }

        public SerializableSmtpDetails SerializableSmtpDetails { get; set; }
    }
我想问一个关于Parallel.For(或C#.Net中的任何其他多线程方法)的问题。我必须构建一个多线程的MailerWindows服务,它将尽可能快地向所有收件人发送邮件。我从包含电子邮件和SmtpDetails的数据库中获取序列化行,然后在代码中反序列化它们

一封电子邮件可能有1000个收件人,因此在双核机器(开发机器)上至少可以同时运行2个线程。所以我用平行线,为了做到这一点。我读过关于LocalInit委托的文章,它为每个线程运行一次

代码:

int itemCount = serMailObj.ReceipientList.Count;

                    Parallel.For(0, itemCount, () =>
                    {
                        return new ThreadLocalStateCache()
                        {
                            Receipient = serMailObj.ReceipientList.Dequeue(),
                            mail = serMailObj.Email,
                            SerializableSmtpDetails = serSmtpObj
                        };
                    }
     , doWork, (x) => { });

 private static ThreadLocalStateCache doWork(int instance, ParallelLoopState state, ThreadLocalStateCache threadInstance)
        {
            KeyValuePair<string, string> kvp = threadInstance.Receipient;
            SerializableSmtpDetails serSmtpObj = threadInstance.SerializableSmtpDetails;
            MailMessage email = threadInstance.mail;

                email.To.Add(new MailAddress(kvp.Key, kvp.Value));

                SmtpClient client = new SmtpClient();
                client.Credentials = new System.Net.NetworkCredential(serSmtpObj.UserName, serSmtpObj.Password);
                client.Host = serSmtpObj.Host;
                client.Port = serSmtpObj.Port;
                client.EnableSsl = serSmtpObj.EnableSSL;

                    try 
                    {           
                        client.Send(email);
                        Console.WriteLine("sending mail....");
                    }
                    catch (Exception)
                    {

                        throw;
                    }


            return null;
        }

public class ThreadLocalStateCache
    {
        public KeyValuePair<string, string> Receipient { get; set; }

        public MailMessage mail { get; set; }

        public SerializableSmtpDetails SerializableSmtpDetails { get; set; }
    }
int itemCount=serMailObj.ReceipientList.Count;
Parallel.For(0,itemCount,()=>
{
返回新的ThreadLocalStateCache()
{
Receipient=serMailObj.ReceipientList.Dequeue(),
mail=serMailObj.Email,
SerializableSmtpDetails=serSmtpObj
};
}
,doWork,(x)=>{};
私有静态ThreadLocalStateCache doWork(int实例、ParallelLoopState状态、ThreadLocalStateCache threadInstance)
{
KeyValuePair kvp=threadInstance.Receipient;
SerializableSmtpDetails serSmtpObj=threadInstance.SerializableSmtpDetails;
MailMessage email=threadInstance.mail;
email.To.Add(新邮件地址(kvp.Key,kvp.Value));
SmtpClient=新的SmtpClient();
client.Credentials=new System.Net.NetworkCredential(serSmtpObj.UserName,serSmtpObj.Password);
client.Host=serSmtpObj.Host;
client.Port=serSmtpObj.Port;
client.enablesl=serSmtpObj.enablesl;
尝试
{           
发送(电子邮件);
Console.WriteLine(“发送邮件…”);
}
捕获(例外)
{
投掷;
}
返回null;
}
公共类ThreadLocalStateCache
{
公钥值对接收器{get;set;}
公共邮件{get;set;}
public SerializableSmtpDetails SerializableSmtpDetails{get;set;}
}
上面的代码非常简单。localInit委托为每个线程构造一个本地对象。然后doWork尝试处理队列

问题:

int itemCount = serMailObj.ReceipientList.Count;

                    Parallel.For(0, itemCount, () =>
                    {
                        return new ThreadLocalStateCache()
                        {
                            Receipient = serMailObj.ReceipientList.Dequeue(),
                            mail = serMailObj.Email,
                            SerializableSmtpDetails = serSmtpObj
                        };
                    }
     , doWork, (x) => { });

 private static ThreadLocalStateCache doWork(int instance, ParallelLoopState state, ThreadLocalStateCache threadInstance)
        {
            KeyValuePair<string, string> kvp = threadInstance.Receipient;
            SerializableSmtpDetails serSmtpObj = threadInstance.SerializableSmtpDetails;
            MailMessage email = threadInstance.mail;

                email.To.Add(new MailAddress(kvp.Key, kvp.Value));

                SmtpClient client = new SmtpClient();
                client.Credentials = new System.Net.NetworkCredential(serSmtpObj.UserName, serSmtpObj.Password);
                client.Host = serSmtpObj.Host;
                client.Port = serSmtpObj.Port;
                client.EnableSsl = serSmtpObj.EnableSSL;

                    try 
                    {           
                        client.Send(email);
                        Console.WriteLine("sending mail....");
                    }
                    catch (Exception)
                    {

                        throw;
                    }


            return null;
        }

public class ThreadLocalStateCache
    {
        public KeyValuePair<string, string> Receipient { get; set; }

        public MailMessage mail { get; set; }

        public SerializableSmtpDetails SerializableSmtpDetails { get; set; }
    }
  • 我收到了每个收件人的多封邮件。似乎电子邮件对象正在线程之间共享

  • 有时发送邮件失败

  • 请解释如何在每个线程中隔离邮件和smtpclient对象。并处理队列


    编辑1:如果多线程专家能帮助我,请告诉我有没有办法让每个线程都有其局部变量的唯一副本,而不是共享变量。因为MailMessage对象不是不可变的,所以我也不能创建它的克隆。除了在每个线程中反序列化它(这将确保创建一个新对象),还有什么神奇的方法可以实现这一点吗?

    下面可能是一个问题:

    serMailObj.ReceipientList.Dequeue()
    
    尝试使用ConcurrentQueue(.NET 4)或设置锁,以便一次一个线程可以将其出列

    还要确保任何地方的并发类或锁都可以从线程访问共享资源

    队列)可以支持 同时多个读卡器,只要 因为集合未被修改。 即便如此,通过 收藏本质上不是一种艺术 线程安全程序。保证 枚举期间的线程安全性 可以在运行期间锁定集合 整个枚举。允许 要由多个用户访问的集合 阅读和写作的线索,你 你必须实现你自己的 同步


    由于
    doWork()
    返回
    null
    可能会出现问题。正如我在回答时所学到的,线程本地对象应该在并行的后续调用之间传递。对于同一线程中的主体,因为它应该作为累加器工作;看见现在还不清楚返回
    null
    时会发生什么,但我会解决这个问题,看看它是否有区别。

    假设我的理解是正确的。由于出列是在LocalInit委托中进行的,因此每个线程应该只运行一次。我更担心正在共享的电子邮件对象。是的,但是如果多个线程同时尝试出列,它可能会变成不一致状态。最好使用ConcurrentQueue我想你考虑过使用ThreadPool吗?不确定使用并行任务是否能获得巨大的好处。是的。我已经尝试过使用线程池。但是由于我是一个多线程的新手,所以我无法理解为此而得到的代码。从SO那里得到了代码。代码的联锁位和减量位。parallel.for对我来说似乎很容易,因为它为我处理同步。