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

Почему моя многопоточность медленнее, чем моя нить?

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

Итак, я сделал небольшую консольную программу для собственного понимания объекта потоковой обработки в Visual Studio 2013. Мой процессор - это Intel Core i7, который может использовать несколько потоков.

Мой код:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {

        static TimeSpan MTTime;
        static TimeSpan STTime;

        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();


            Console.WriteLine(Environment.NewLine + "---------------Multi Process-------------" + Environment.NewLine);

            Thread th1 = new Thread(new ParameterizedThreadStart(Process));
            Thread th2 = new Thread(new ParameterizedThreadStart(Process));
            Thread th3 = new Thread(new ParameterizedThreadStart(Process));
            Thread th4 = new Thread(new ParameterizedThreadStart(Process));

            th1.Start("A");
            th2.Start("B");
            th3.Start("C");
            th4.Start("D");

            th1.Join();
            th2.Join();
            th3.Join();
            th4.Join();

            stopwatch.Stop();
            MTTime = stopwatch.Elapsed ;

            Console.WriteLine(Environment.NewLine + "---------------Single Process-------------" + Environment.NewLine);


            stopwatch.Reset();
            stopwatch.Start();

            Process("A");
            Process("B");
            Process("C");
            Process("D");

            stopwatch.Stop();
            STTime = stopwatch.Elapsed;

            Console.Write(Environment.NewLine + Environment.NewLine + "Multi  : "+ MTTime + Environment.NewLine + "Single : " + STTime);


            Console.ReadKey();
        }

        static void Process(object procName)
        {
            for (int i = 0; i < 100; i++)
            {
                Console.Write(procName);
            }
        }
    }
}

Изображение результатов:

enter image description here

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

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

4b9b3361

Ответ 1

Из официальной Console документации

Операции ввода/вывода, которые используют эти потоки, синхронизированы, что означает что несколько потоков могут читать или записывать потоки. Эта означает, что методы, которые обычно являются асинхронными, например TextReader.ReadLineAsync, выполняется синхронно, если объект представляет консольный поток

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


UPDATE
Я предлагаю вам взглянуть на Parallel.ForEach

Ответ 2

Обратите внимание, что Process записывает на консоль (и в основном ничего не делает), и вывод на консоль (который здесь действует как своего рода общий ресурс) медленный и нуждается в синхронизации с другими потоками.

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

Ответ 3

Еще одно, что нужно учитывать: вы страдаете от накладных расходов для создания потоков и объединения. Кроме того, вы можете получить небольшое повышение производительности, используя threadpool, посмотрите здесь:

https://msdn.microsoft.com/en-us/library/system.threading.threadpool.queueuserworkitem%28v=vs.110%29.aspx

Ответ 4

OK! спасибо в Assa и Codor, чтобы поместить мой разум в нужное место! Я, наконец, создаю небольшую консольную программу, которая показывает, что все очень ясно. Заключительная многозадачность намного быстрее, когда используется тяжелая обработка. Просто прочитайте мой код, и вы легко поймете.

Результат:

enter image description here

Код:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        //Timer for speed guidance
        static Stopwatch stopwatch;
        //Data i use for generate time
        static List<int> timeData;
        static void Main(string[] args)
        {
            stopwatch = new Stopwatch();
            timeData = new List<int> { 1000, 800, 200, 700, 600, 300, 800, 100, 200, 300, 655, 856, 695, 425 };

            ////-------------------------- SINGLE THREAD ------------------------------/////
            Console.WriteLine("-------------------------------------------------");
            Console.WriteLine("             Single Threading Process            ");
            Console.WriteLine("-------------------------------------------------");
            Console.WriteLine("   Process Time        Thread ID                 ");
            Console.WriteLine("-------------------------------------------------");
            stopwatch.Reset();
            stopwatch.Start();
            //For each normal that use only 1 thread
            foreach(int i in timeData)
            {
                Process(i);
            }

            stopwatch.Stop();
            //Total time that the program take for making the process happen
            Console.WriteLine("*Total : " + stopwatch.Elapsed );

            ////-------------------------- Mulit Multiple ------------------------------/////

            Console.WriteLine("-------------------------------------------------");
            Console.WriteLine("-------------------------------------------------");
            Console.WriteLine("             Multi Threading Process            ");
            Console.WriteLine("-------------------------------------------------");
            Console.WriteLine("   Process Time        Thread ID                 ");
            Console.WriteLine("-------------------------------------------------");
            stopwatch.Reset();
            stopwatch.Start();
            //for each thats use Multiple thread fr the process (can be made with parallel.invoke or Task Library or Thread Library)
            Parallel.ForEach(timeData, (i) => Process(i));
            //Total time that the program take for making the process happen
            Console.WriteLine("*Total : " + stopwatch.Elapsed);
            Console.WriteLine("-------------------------------------------------");
            Console.ReadKey();
        }

        // Methode for sumulating long processing
        static void Process( int time)
        {
            stopwatch.Reset();
            stopwatch.Start();
            //sleep time simulate the IO portion of the process
            Thread.Sleep(time);
            // The loop simulate de algoritme type of precessing
            for (int i = 0; i < time*1000000; i++){}
            stopwatch.Stop();
            Console.WriteLine( stopwatch.Elapsed + "         " + Thread.CurrentThread.ManagedThreadId.ToString());          
        }


    }
}

Ответ 5

По-прежнему поражает меня, как много людей склонны думать: пусть больше используют потоки, наш код будет работать быстрее. Это не работает.

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

Если вы хотите, чтобы ваш код работал быстрее, вы должны сначала проверить, что заставляет его работать медленно. Профилируйте свой код. Найдите узкое место.

И помните, что Преждевременная оптимизация - это корень всего зла

В вашем случае узким местом - единственным наиболее доступным ресурсом, который блокирует все действия, является поток вывода консоли. Он имеет один экземпляр и работает относительно медленно. Даже одно ядро ​​не будет использоваться на 100% для печати на нем как можно быстрее.