Почему объединение потоков ведет себя по-другому в этом примере? - PullRequest
2 голосов
/ 16 июня 2011

Обновлен вопрос, чтобы быть более общим:

У меня есть следующий код.Когда вы меняете расположение потоков [i] .Join (), вы получаете другой вывод.

static void ThreadedWorker(int startIndex, int endIndex)
{
    Console.WriteLine("Working from results[ " + startIndex +"] to results["+endIndex+"]");
}

static void Main(string[] args)
{
    int threadCount = System.Environment.ProcessorCount;
    int calculationCount = 500; //the number of array elements we'd be iterating over if we were doing our work
    int threadDataChunkSize = calculationCount / threadCount;
    if (threadDataChunkSize < 1) threadDataChunkSize = 1; //just in case we have loads of threads

    Thread[] threads = new Thread[threadCount];
    for (int i = 0; i < threadCount; i++)
    {
        threads[i] = new Thread(() => ThreadedWorker(threadDataChunkSize * i, threadDataChunkSize*(i+1)));
        threads[i].Start();
        //threads[i].Join(); //****Uncomment for correct behaviour****
    }

    for (int i = 0; i < threadCount; i++)
    {
        //threads[i].Join(); //****Uncomment for incorrect behaviour****
    }

    Console.WriteLine("breakhere");
}

Когда Join() находится в первом цикле, создавая последовательное поведение, вы получаете вывод

Working from results[ 0] to results[125]
Working from results[ 125] to results[250]
Working from results[ 250] to results[375]
Working from results[ 375] to results[500]

Когда Join() находится во втором цикле, создавая параллельное поведение,вы получаете недетерминированный вывод что-то вроде:

Working from results[ 375] to results[500]
Working from results[ 375] to results[500]
Working from results[ 500] to results[625]
Working from results[ 500] to results[625] (i is sometimes more than it should ever be!)

Я подозреваю, что лямбда-выражение каким-то образом вызывает проблему.Надеемся, что это перефразирование также демонстрирует, что это не просчет границ или другое злоупотребление моими массивами!


Первоначальный вопрос был менее общим и использовал startIndex и endIndex для перебора массива байтов при выполнении работы.Я описал ThreadedWorker как «не работающий», поскольку иногда он обновлял массив результатов, а иногда нет.Теперь кажется, что он назывался, но startindex и endindex были искажены.

Ответы [ 3 ]

5 голосов
/ 16 июня 2011

Первый код Join s для каждого потока сразу после его запуска, до запуска следующего потока.

Следовательно, все потоки выполняются последовательно.

Второй код запускает сразу все потоки, а затем Join все сразу.

Поэтому потоки одновременно работают с одними и теми же данными.

Второй код, вероятно, не работает, потому что ваш код или данные не являются потокобезопасными.

1 голос
/ 17 июня 2011

Ваше решение правильное, но вы неправильно понимаете проблему.

Лямбда-выражения потокобезопасны.

Однако все ваши лямбда-выражения совместно используют одну и ту же переменную i.
Следовательно, если один из потоков запускается после того, как цикл переходит на следующую итерацию, он получит более новое значение i.

Объявляя отдельную переменную внутри цикла, вы заставляете каждую лямбду использовать свою собственную переменную, которая никогда не меняется.

0 голосов
/ 17 июня 2011

И чтобы закончить обновление на мой вопрос, я дошел до сути.Поскольку лямбда-выражение не является потокобезопасным, мне нужно хранить i во временной переменной на каждой итерации цикла:

for (int i = 0; i < threadCount; i++)
{
    int temp = i;
    threads[temp] = new Thread(() => ThreadedMultiplier(threadDataChunkSize * temp, threadDataChunkSize * (temp + 1)));
threads[temp].Start();
}

for (int i = 0; i < threadCount; i++)
{
    //threads[i].Join(); //****Uncomment for  correct + parallel behaviour at last!****
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...