Начало потока с / без делегата () - PullRequest
14 голосов
/ 09 ноября 2010

Какая разница между:

new Thread(new ThreadStart(SomeFunc))

и:

new Thread( delegate() { SomeFunc();} )

Этот код выдает странные результаты на моем компьютере:

public class A
{
    int Num;

    public A(int num)
    {
        Num = num;
    }

    public void DoObj(object obj)
    {
        Console.Write(Num);
    }

    public void Do()
    {
        Console.Write(Num);
    }
}

/////// in void main()

for (int i = 0; i < 10; i++)
{
    (new Thread(new ThreadStart((new A(i)).Do))).Start(); // Line 1
    (new Thread(new ThreadStart(delegate() { (new A(i)).Do(); }))).Start(); // Line 2
    (new Thread(delegate() { (new A(i)).Do(); })).Start(); // Line 3
}

Если только строка1 выполняется, вывод выглядит примерно так:

0 2 3 1 5 6 4 7 8 9

, что нормально, но если выполняется строка 2 или 3, выведитеэто:

3 3 3 5 5 7 7 9 9 10

Есть несколько чисел и 10, что довольно странно, что цикл никогда не запускается с№ 10. Что за хитрость стоит за этим?

Спасибо.

Ответы [ 3 ]

15 голосов
/ 09 ноября 2010

С делегатом вы захватываете i.

Разница в том, что с new ThreadStart((new A(i)).Do)) вы создаете новый экземпляр A в цикле for с i в качестве параметра. Это означает, что в этот момент значение i берется и отправляется в конструктор. Таким образом, отправляемый вами делегат не относится к созданию A, но вы фактически отправляете в конструктор делегат метода Do экземпляра A.

Однако, с delegate() { (new A(i)).Do(); }) (оба) вы отправляете ссылку i в поток.

Затем потоку требуется некоторое время для запуска, и тем временем цикл for продолжается. К тому времени, когда i используется в делегате (то есть поток запущен), цикл for перешел на 3, и это то, что вы видите. То же самое касается второго и третьего потока. Три потока запущены, но ждите, пока начальный поток завершит некоторую работу. Затем созданные потоки включаются (потоки 1, 2 и 3) и выполняют свою работу. Windows возвращается к потоку с циклом for и переходит к запуску потоков 4 и 5.

Некоторые материалы для чтения:

4 голосов
/ 09 ноября 2010

Чтобы ответить на ваш первый пункт, delegate() { SomeFunc();} создает функцию, которая вызывает SomeFunc(), тогда как без использования delegate() просто используется функция SomeFunc непосредственно как метод ThreadStart.

В вашем втором вопросе вы сталкиваетесь с деталями реализации анонимных функций C #. Все три ссылки на i относятся к одинаковому i, который увеличивается три раза. У вас есть условие состязания между тремя функциями, которое означает, что i может быть увеличен несколько раз перед запуском запущенных потоков.

1 голос
/ 19 октября 2015

'Когда вызывается конструктор для объекта A?'помогает ответить на вопрос.

new ThreadStart((new A(i)).Do))

Когда эта строка кода выполняется - вызывается конструктор и сохраняется ссылка на функцию .Do во вновь созданном объекте Aделегат ThreadStart.

В строках 2 и 3 вы используете анонимный делегат (представлен в C # 2.0).

delegate() { (new A(i)).Do(); })

Содержимое анонимного делегата не выполняется, пока не будет вызван делегат;в этом случае потоком, когда для этого назначается временной интервал.

Переменная i объявляется только один раз в начале цикла for, и на содержимое делегата есть ссылка (делегаты будутdo this) - когда код выполняется, он должен получить значение i во время выполнения.

Это объясняет значение 10. У меня есть значение 10, когда цикл завершится.Если один из потоков выполняется после завершения цикла, он выведет 10.

Чтобы избежать проблемы с несколькими числами, вы можете создать локальную копию переменной цикла.Делегат сохранит ссылку на собственную версию icopy;

for (int i = 0; i < 10; i++)
{
     int icopy = i;
     (new Thread(new ThreadStart(delegate() { (new A(icopy)).Do(); }))).Start();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...