ThreadPool.QueueUserWorkItem внутри foreach использует один и тот же набор данных

#c# #multithreading #threadpool #parallel.foreach

#c# #многопоточность #threadpool #parallel.foreach

Вопрос:

В приведенной ниже функции методу всегда передается один и тот же user объект DoRestCall

(У меня есть регистрация в DoRestCall методе, и он имеет те же первые данные в user объекте) Нужно ли мне использовать Parallel.ForEach вместо Threadpool

 private void CreateUser(DataServiceCollection<User> epUsers)
{
    foreach (User user in epUsers)
    {
        try
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(f =>
            {
                DoRestCall(string.Format("MESSAGE-TYPE=UserEnrollmentCreateamp;PAYLOAD={0}",
                                         GenarateRequestUserData(user)), true);
            }));
        }
        catch (Exception ex)
        {
            _logger.Error("Error in CreateUser "   ex.Message);
        }
    }
}
 

Комментарии:

1. Если вы выполняете вызов REST, зачем использовать ThreadPool работу с привязкой к вводу-выводу? Какая версия . Вы используете NET framework

2. Я заставляю rest вызывать какие-либо недостатки? C # и .net4.0

3. ДА. Вы выделяете потоки ThreadPool только для того, чтобы блокировать ожидание возврата вызова REST, это пустая трата ресурсов.

4. Есть ли альтернатива для этого?

Ответ №1:

Проблема в том, как обрабатываются переменные цикла при использовании в лямбда-выражении или анонимных методах. Лямбда-выражение видит текущее значение переменной цикла во время выполнения лямбда-выражения. Я считаю, что это поведение было изменено в C # 5.0, но еще не пробовал.

Вам нужно сохранить текущего пользователя в переменной внутри цикла foreach и использовать ее вместо переменной цикла (кроме того, ваш try / catch не улавливает никаких исключений внутри вашего WaitCallback , см. Исправление Ниже):

 foreach (User user in epUsers)
{
    User currentUser = user;
    ThreadPool.QueueUserWorkItem(new WaitCallback(f =>
    {
        try
        {
            DoRestCall(string.Format("MESSAGE-TYPE=UserEnrollmentCreateamp;PAYLOAD={0}",
                                     GenarateRequestUserData(currentUser)), true);
        }
        catch (Exception ex)
        {
            _logger.Error("Error in CreateUser "   ex.Message);
        }
    }));
}
 

Комментарии:

1. foreach был изменен в C # 5.0, for не был, так что вы полностью правы по всем пунктам.