2014-01-05 118 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不被用作共享資源。所以如果每次創建新對象都很安全。