2014-01-05 4 views
1

Оператор блокировки C# позволяет только одному потоку обращаться к объекту за раз. В цикле Parallel.ForEach не было бы быстрее создавать новый объект (локальную переменную) внутри цикла, а не использовать переменную поля, так как в этом случае нити никогда не блокируются? Какими будут плюсы/минусы каждого из них?Блокировка потоков против создания нового объекта

Я использую приведенный ниже код, кажется, что создание локального var вместо использования блокировки в поле немного быстрее.

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, по одному для каждого потока, по сравнению с одним для каждой задачи/итерации? –

+0

Да. Таким образом, вы создаете только 3 smtp – MRB

+0

где используется переменная состояния? И есть ли необходимость в переменной smtpClient? Не могли бы вы просто использовать smtp? –

0

В простом блокировки предпочтительнее, когда есть общий ресурс. Как и в вашем случае, достаточно просто, чтобы SMTP CLIENT не использовался как общий ресурс. поэтому его вполне безопасно, если вы каждый раз создаете новый объект.

Смежные вопросы