[C#] Wielowątkowość a wielordzeniowość


(Marcin Obala) #1

Witam

Wpadł mi do głowy taki pomysł (prawie usnąłem czekając aż program przemieli dane), aby wykorzystać trochę moc procesora do przetwarzania plików. Mam program który przemielenia multum danych i przy mieleniu wykorzystuje 25% procesora (4 rdzenie), lub koło 8 (12 rdzeni). Utworzyłem sobie program który generuje mi zadaną liczbę wątków, dzieli dane na porcje ale teraz, jak odpalić każdy wątek w innym rdzeniu? Czy jest jakaś prosta metoda? Ktoś coś próbował?

Pozdrawiam


(Ljasiecki) #2

Witam,

Proponuję zapoznać się z biblioteką TPL dostępną w .NET 4.

Wiele przykładów na: http://msdn.microsoft.com/en-us/library/dd460693.aspx

Pozdrawiam!


(Tomek Matz) #3

Ewentualnie z klasą ThreadPool http://www.dotnetperls.com/threadpool (zarówno TPL jak i ThreadPool będzie dobrym rozwiązaniem). Ogólnie jest tak, że Ty nie musisz myśleć o tym jak przydzielić dany wątek do danego rdzenia. To już jest robione za Ciebie w tle.


(CzoQś) #4

ThreadPool będzie dla Ciebie bardzo dobrym rozwiązaniem...

Tworzysz pulę wątków i nie musisz martwić się który wątek wykona się na którym rdzeniu - to system operacyjny ma za zadanie tak porozdzielać wątki, aby uzyskać jak najlepszą wydajność, równomierne obciążenie poszczególnych rdzeni itp..

Pozdrawiam


(Marcin Obala) #5

matzu a mógłbyś podać jakieś przykład? Wiadomo, na przykładach najlepiej widać wszystko.

Ja w swoim kodzie mam tablicę wątków

Thread[] watki = new Thread[Ilość_wątków];

Czy da się to jakoś łatwo podpiąć pod to co polecacie? Tak najlepiej żebym nie musiał kodu działania samych wątków zmieniać. Bo przyznam się nie miałem czasu przestudiować.


(Tomek Matz) #6

Przykłady znajdziesz chociażby na stronach msdn (lub w linku, który podałem). Mimo wszystko przygotowałem przykładowy programik, który IMO powinien kilka kwestii wyjaśnić :slight_smile: Program oblicza kilka silni w osobnych wątkach - zarówno przy użyciu Thread Pool jak i TPL (do metody obliczającej silnię dodałem wstrzymanie bieżącego wątku na dwie 2ms, żeby zasymulować dłuższą operację - możesz zobaczyć jakie będą wyniki, gdy zmienisz wartość z 2ms na np. 1ms lub 5ms). Program ten wstrzymuje działanie dopóki wszystkie silnie nie zostaną obliczone. Dopiero, gdy wszystkie silnie zostaną wyliczone, program wyświetla wyniki (w takiej kolejności w jakiej wywołane zostały poszczególne wątki) i kontynuuje swoje działanie.

using System;

using System.Diagnostics;

using System.Threading;

using System.Threading.Tasks;


namespace ParallelProgrammingDemo

{

    internal class Program

    {

        private static Random random = new Random(DateTime.Now.Millisecond);


        private static void Main(string[] args)

        {

            int[] numbers = new int[50];

            for (int i = 0; i < numbers.Length; i++)

                numbers[i] = random.Next(1, 20); // 

            Stopwatch watch = new Stopwatch();

            watch.Start();

            Result[] results = CalculateFactorials(numbers);

            watch.Stop();

            Console.WriteLine("--- Single-threaded programming ---");

            Display(results);

            Console.WriteLine("Elapsed milliseconds: {0}.\n", watch.ElapsedMilliseconds.ToString());


            watch.Reset();

            watch.Start();

            results = CalculateFactorialsWithThreadPool(numbers);

            watch.Stop();

            Console.WriteLine("--- Parallel programming - ThreadPool ---");

            Display(results);

            Console.WriteLine("Elapsed milliseconds: {0}.\n", watch.ElapsedMilliseconds.ToString());


            watch.Reset();

            watch.Start();

            results = CalculateFactorialsWithTPL(numbers);

            watch.Stop();

            Console.WriteLine("--- Parallel programming - TPL ---");

            Display(results);

            Console.WriteLine("Elapsed milliseconds: {0}.\n", watch.ElapsedMilliseconds.ToString());


            Console.ReadKey();

        }


        private static Result[] CalculateFactorials(int[] numbers)

        {

            Result[] results = new Result[numbers.Length];


            for (int i = 0; i < numbers.Length; i++)

            {

                Result result = new Result(numbers[i]);

                result.Factorial = CalculateFactorial(result.Number);


                results[i] = result;

            }


            return results;

        }


        private static Result[] CalculateFactorialsWithThreadPool(int[] numbers)

        {

            ManualResetEvent[] resetEvents = new ManualResetEvent[numbers.Length];

            Result[] results = new Result[numbers.Length];


            for (int i = 0; i < numbers.Length; i++)

            {

                ManualResetEvent resetEvent = new ManualResetEvent(false);

                Result result = new Result(numbers[i]);


                resetEvents[i] = resetEvent;

                results[i] = result;


                ThreadPool.QueueUserWorkItem(delegate

                {

                    result.Factorial = CalculateFactorial(result.Number);

                    resetEvent.Set();

                });

            }


            WaitHandle.WaitAll(resetEvents);


            return results;

        }


        private static Result[] CalculateFactorialsWithTPL(int[] numbers)

        {

            Result[] results = new Result[numbers.Length];


            int from = 0;

            int to = numbers.Length;


            Parallel.For(from, to, index =>

                {

                    Result result = new Result(numbers[index]);

                    result.Factorial = CalculateFactorial(result.Number);


                    results[index] = result;

                });


            return results;

        }


        private static void Display(Result[] results)

        {

            Console.WriteLine("{0,-10} {1,-10}", "Number", "Factorial");

            foreach (Result result in results)

                Console.WriteLine("{0,-10} {1,-10}", result.Number, result.Factorial);

        }


        private static long CalculateFactorial(long value)

        {

            checked

            {

                if (value == 0)

                {

                    Thread.Sleep(2);

                    return 1;

                }

                else

                    return value * CalculateFactorial(value - 1);

            }

        }

    }


    internal class Result

    {

        public Result(int number)

        {

            Number = number;

            Factorial = 0;

        }


        public int Number { get; private set; }

        public long Factorial { get; set; }

    }

}