2014-01-05 54 views
1

C#锁定语句一次只允许一个线程访问对象。 在Parallel.ForEach循环中,在循环中创建新对象(局部变量)不是更快,而不是使用字段变量,因为这样就不会阻塞任何线程? 每种方式的优点/缺点是什么?线程锁定与创建新对象

我使用下面的代码,似乎创建一个本地变量,而不是在字段上使用锁定稍快。

nb。 toEmails变量中有3个电子邮件字符串。

//Method 1 with lock statement takes 14092ms 
List<string> toEmails = getListOfToEmails(); 
Object locker = new object(); 
SmtpClient smtpClient = getSmtpObject();//smtpClient is used here as a field 
Parallel.ForEach(toEmails, toEmail => 
{ 
    string emailBody = getEmailBody(toEmail); 

    MailMessage mailMessage = getMailMesssageObject(emailBody, toEmail); 

    lock (locker) 
    { 
     smtpClient.Send(mailMessage); 
    } 
}); 

//Method 2 without lock statement (creating a new local var each iteration) takes 13947ms 
List<string> toEmails = getListOfToEmails(); 
Parallel.ForEach(toEmails, toEmail => 
{ 
    SmtpClient smtpClient = getSmtpObject();//smtpClient is used here as a local var 

    string emailBody = getEmailBody(toEmail); 

    MailMessage mailMessage = getMailMesssageObject(emailBody, toEmail); 

    smtpClient.Send(mailMessage); 
}); 
+1

如果您的共享对象具有某种您关心的独特状态信息,那么您不能仅仅创建新的状态信息。 –

+0

如果我记得正确TPL也支持其循环中的线程局部变量。 – Dirk

+0

通常,通过创建一个线程池,可以在启动时创建一个“重”,然后在池队列中循环,为“重”创建数据,从而避免不断创建繁重的对象和线程。换句话说,我可能不会使用任何一种替代品。 –

回答

4

使用此超载ForEach

Parallel.ForEach(toEmails,() => getSmtpObject() , (toEmail, state, smtp) => 
{ 
    SmtpClient smtpClient = smtp; 

    string emailBody = getEmailBody(toEmail); 

    MailMessage mailMessage = getMailMesssageObject(emailBody, toEmail); 

    smtpClient.Send(mailMessage); 

    return smtp; 
}, smtp => {}); 

第二个参数是一个代表,你可以用它来创建线程局部数据。在循环的每次迭代中,您将获得本地数据,您可以更改它并返回到下一次迭代。

最后一个参数是另一个代表,这将在每一个任务

+0

所以假设,假设你有9次迭代,并且有3个线程每次运行3次迭代。 这种方式更好,因为您将只创建3个SmtpClient对象,每个线程对应一个,而每个Task /迭代对应一个对象? –

+0

是的。这样你只能创建3 smtp – MRB

+0

其中是使用的状态变量? 是否有任何需要smtpClient变量?你不能只使用smtp吗? –

0

简单末被称为锁定最好的时候有一个共享的资源。就你的情况而言,它非常简单,SMTP CLIENT不被用作共享资源。所以如果每次创建新对象都很安全。