Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Создание множества потоков


Автор: Lex101 7.3.2011, 22:12
Добрый вечер! Есть задача - многопоточные векторные вычисления. Есть 3 массива, в первых двух рандомные значения, а в третьем - результат их перемножения. Размерность массивов - ну скажем 100млн элементов. И всё это дело нужно разбивать на огромное количество поток, причём количество произвольное - от 500 до 600 допустим.

Я создаю потоки в цикле, большинство выведенных мне элементов - нули. При дебагинге заметил, что при создании нового потока в цикле на следующей итерации, предыдущий поток умирает, так и не успев завершить все вычисления в заданном ему диапазоне. Очень прошу помощи!

Код

using System;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        //создаём массив потоков
        static public Thread[] trArr;
        //создаём массивы для вычислений
        static public sbyte[] Arr1;
        static public sbyte[] Arr2;
        static public sbyte[] Arr3;
        //создаём массив, в котором будет храниться диапазон вычислений
        static public int[] ArrInt;

        static void Main(string[] args)
        {
            //инициализируем массивы
            Random r1 = new Random();
            Random r2 = new Random();
            Arr1 = new sbyte[100000000];
            Arr2 = new sbyte[100000000];
            Arr3 = new sbyte[100000000];
            ArrInt = new int[2];
            trArr = new Thread[512];
            for (int i = 0; i < 100000000 - 1; i++)
            {
                Arr1[i] = (sbyte)r1.Next(1, 10);
                Arr2[i] = (sbyte)r2.Next(1, 10);
                
            }
            ArrInt[0] = 0;
            ArrInt[1] = 100000000 - 1;

            Console.WriteLine("Память выделена, нажмите Enter");
            Console.ReadLine();
            //создаём множество потоков, которые будут выполнять метод Count и передаём в него диапазон вычислений из ArrInt
            for (int i = 0; i < 512; i++)
            {
                if (i == 511)
                {
                    trArr[i] = new Thread(new ParameterizedThreadStart(Count));
                    ArrInt[0] = (i + 1) * 195312;
                    ArrInt[1] = 100000000;
                    trArr[i].Start(ArrInt);
                }
                else
                {
                    trArr[i] = new Thread(new ParameterizedThreadStart(Count));
                    ArrInt[0] = 195312 * i;
                    ArrInt[1] = ((i + 1) * 195312) - 1;
                    trArr[i].Start(ArrInt);
                }
            }
            //ждём завершения всех потоков
            for (int i = 0; i < 512; i++)
            {
                trArr[i].Join();
            }
            //выводим некоторые значения из результирующего массива
            Console.WriteLine("Готово");
            Console.WriteLine(Arr3[0]);
            Console.WriteLine(Arr3[50]);
            Console.WriteLine(Arr3[49424]);
            Console.WriteLine(Arr3[99463148]);
            Console.ReadLine();
        }

        //метод умножения элементов первых двух массивов.
        static void Count(object o)
        {
            int[] bArr = (int[])o;
            for (int i = bArr[0]; i < bArr[1] - 1; i++)
            {
                Arr3[i] = (sbyte)(Arr1[i] * Arr2[i]);
            }
           
        }
    }
}

Автор: Экскалупатор 8.3.2011, 00:35
Цитата(Lex101 @  7.3.2011,  21:12 Найти цитируемый пост)
Размерность массивов - ну скажем 100млн элементов.

хотел бы я посмотреть как ты будешь все это обрабатывать.

Цитата(Lex101 @  7.3.2011,  21:12 Найти цитируемый пост)
И всё это дело нужно разбивать на огромное количество поток, причём количество произвольное - от 500 до 600 допустим.

с какой целью? у тебя есть в распоряжении 500-600 ядер в процессоре? ну или 500-600 процессоров? думаю нет, тогда в чем прикол?
все равно выполняться будут по очереди, и будут просто астрономические потери на создание/переключение/уничтожение потоков.

Добавлено @ 00:45
Цитата(Lex101 @  7.3.2011,  21:12 Найти цитируемый пост)
for (int i = 0; i < 100000000 - 1; i++)

тут ты делаешь не 100 000 000 итераций а 99 999 999, а значений в массиве все же 100 000 000, последнее не создается

а как ты отследил что поток "умирает"? может просто управление переходит к другому потоку? а предыдущий засыпает?

Автор: Lex101 8.3.2011, 00:59
1) А что такого в такой большой размерности?
памяти сожрёт не мало, и всего-то.
2) Про практическое применение я не говорил, а сказал, что есть такая задача!
3) ну ошибся на чуток, не обработается один элемент, не страшно

Поток помечен как остановленный Stopped - а не как спящий.

Надеюсь бесполезных замечаний больше не будет, очень надеюсь на помощь в решении этой задачи!

Автор: Экскалупатор 8.3.2011, 01:37
Lex101, ну ладно, не буду делать "бесполезных замечаний". 
по существу.
я думаю все потому что у тебя в методе идет работа с одними и теми же данными, и потоки не могут их между собой поделить, потому что метод статический(т.е. один на все приложение) и данные тоже.
для примера, если сделать метод Count не статическим, и убрать из него использование статических переменных, то он начинает выполняться оооооочень долго.
для примера вот так:
Код

 public void Count(object o)
        {
            for (int i = 0; i < 1000; ++i)
            {
                Console.WriteLine(i);
            }
            Console.WriteLine(Thread.CurrentThread.Name + " end");
        }


при этом он долго и нудно(у меня минут 5) выводит числа в консоль не останавливаясь, потом пишет что поток завершился.
это было по существу, надеюсь помог. остальное это архитектура.

думаю поможет если выполнять потоки по очереди(тут лучше запускать в цикле один и тот же алгоритм, если уж так принципиально делать 500-600 итераций), или сделать что бы каждый поток обрабатывал свои данные).

или нужно реализовать потокобезопасный способ доступа к данным.

но это опять же личные наблюдения

Автор: Lex101 8.3.2011, 02:17
Благодарю за советы!
считает долго только из-за того, что выводит на консоль. 
А делать метод нестатическим - значит фигачить его в класс. Думаю стоит попробовать раскидать логику по классам.

Автор: Экскалупатор 8.3.2011, 02:43
Lex101, думаю правильным будет сделать класс который работает с массивом. попытаюсь объяснить, в твоем случае у тебя массив открыт и его изменяют все кому не лень, отсюда проблемы. если обернуть массивы в класс который будет через какой то метод выполнять одно действие, то потоки не будут иметь доступ на прямую к массиву и не возникнет ситуации когда могут быть проблемы. при этом надо что бы в метод не могло попасть более двух потоков одновременно.


Цитата(Lex101 @  8.3.2011,  01:17 Найти цитируемый пост)
считает долго только из-за того, что выводит на консоль. 

оно же ничего не считает, просто работа распределяется "более-менее равномерно" между всеми создаваемыми потоками, по этому до окончания одно из потоков проходит долго времени, потому что они по сути не выполняются параллельно, у меня стоит двухядерный процессор, можно предположить что одновременно могут выполняться два потока, но вывод на консоль показывает что это не так, все же один поток всегда приоритетнее...

Автор: Lex101 8.3.2011, 12:20
Колоссальное замедление при выводе в консоль связано только с отрисовкой самих элементов в консоли. 

Автор: -Mikle- 8.3.2011, 14:21
По своему опыту подтверждаю на все 100. Любые операции вывода (даже в консоль) всегда очень медленные.

Автор: Экскалупатор 8.3.2011, 14:37
да тут не в том вопрос что медленно, я просто сказал что потоки не прекращают свою работу, и выводят информацию от начала и до конца. то что медленно это детали. просто у человека была проблема что потоки умирали, я показал что так они не умирают.

а консоль не при чем

Автор: -Mikle- 9.3.2011, 18:54
а, точно smile отошли от темы че-то...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)