Подтвердить что ты не робот

Запуск нового потока в цикле 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));

но ни один из потоков не начал.

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

4b9b3361

Ответ 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().

Ответ 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);
         });

}

не проверено, хотя....