Запуск нового потока в цикле foreach
У меня есть список объектов, и я хотел бы перебрать этот список и запустить новый поток, проходящий в текущем объекте.
Я написал пример того, что, как я думал, должен это сделать, но он не работает. В частности, кажется, что потоки перезаписываются на каждой итерации. Это не имеет для меня никакого смысла, потому что я каждый раз создаю новый объект Thread.
Это тестовый код, который я написал
class Program
{
static void Main(string[] args)
{
TestClass t = new TestClass();
t.ThreadingMethod();
}
}
class TestClass
{
public void ThreadingMethod()
{
var myList = new List<MyClass> { new MyClass("test1"), new MyClass("test2") };
foreach(MyClass myObj in myList)
{
Thread myThread = new Thread(() => this.MyMethod(myObj));
myThread.Start();
}
}
public void MyMethod(MyClass myObj) { Console.WriteLine(myObj.prop1); }
}
class MyClass
{
public string prop1 { get; set; }
public MyClass(string input) { this.prop1 = input; }
}
Выход на моем компьютере
test2
test2
но я ожидал, что это будет
test1
test2
Я попытался изменить строки потоков на
ThreadPool.QueueUserWorkItem(x => this.MyMethod(myObj));
но ни один из потоков не начал.
Я думаю, что у меня просто есть недоразумение о том, как потоки должны работать. Может ли кто-нибудь указать мне в правильном направлении и сказать мне, что я делаю неправильно?
Ответы
Ответ 1
Это потому, что вы закрываете переменную в неправильной области. Решением здесь является использование временного в вашем цикле foreach:
foreach(MyClass myObj in myList)
{
MyClass tmp = myObj; // Make temporary
Thread myThread = new Thread(() => this.MyMethod(tmp));
myThread.Start();
}
Подробнее я рекомендую прочитать сообщение Эрика Липперта по этому конкретному вопросу: Закрытие переменной цикла считается вредоносным
Ответ 2
Проблема заключается в том, что вы используете самое текущее значение объекта внутри вашего закрытия. Таким образом, каждый вызов потока смотрит на одно и то же значение. Чтобы обойти это, скопируйте значение в локальную переменную:
foreach(MyClass myObj in myList)
{
MyClass localCopy = myObj;
Thread myThread = new Thread(() => this.MyMethod(localCopy));
myThread.Start();
}
Ответ 3
Согласитесь с Ридом (+1).
Я бы добавил, что если вы используете .NET 4, вы можете захотеть просмотреть параллельную библиотеку задач для решения этого класса проблем. В частности, для этого случая посмотрите Parallel.ForEach().
Ответ 4
если последовательность не имеет значения, чем go для
Parallel.ForEach(myList, obj => this.MyMethod(obj) );
Напишите простой цикл Parallel.ForEach
Ответ 5
Я предпочитаю этот способ:
public void ThreadingMethod()
{
var myList = new List<MyClass> { new MyClass("test1"), new MyClass("test2") };
Parallel.ForEach(myList, new ParallelOptions() { MaxDegreeOfParallelism = 100 },
(myObj, i, j) =>
{
MyMethod(myObj);
});
}
не проверено, хотя....