Многопоточность, асинхронность. Разница во времени выполнения?

Я выполняю три метода, перечитываю текстовый файл, на предмет, кол-ва букв а в нём, я выполняю это тремя способами: 1 последовательно, асинхронно и многопоточно. Вот результат: Wasted the time consistently method 00:00:01.9155446

Wasted the time async method 00:00:00.7132119

Wasted the time multy Thread method 00:00:19.6830515

Подскажите почему последовательное выполнение быстрее всего, это же не логично, что тогда быстрее async or thread. И ещё вопрос что тогда такое Parallel.Onvoke() ? Если асинхронность понятно, это когда в методе есть async and await Многопоточность, это как я понял, если в методе создаётся thread(Method); thread.Start(); А вот эта параллельность это что? Чем отличается от многопоточности?

        public static int CountForThirdMethod { get; set; } = 0;
        static object locker = new object();

        private static int ReadFile(string path)
        {
            int count = 0;
            using (StreamReader sr = new StreamReader(path))
            {
                string line = String.Empty;
                int i = 0;
                while ((line = sr.ReadLine()) != null)
                {
                    if (!String.IsNullOrEmpty(line))
                    {
                        i++;
                        MatchCollection matches3 = Regex.Matches(line, "а");
                        count += matches3.Count;
                        Console.WriteLine($"in line num {i} count symbol \"a\" equal: {matches3.Count}");
                    }
                }
            }
            return count;
        }

        public static async Task<int> ReadFileAsync(string path)
        {
            int count = 0;
            using (StreamReader sr = File.OpenText(path))
            {
                string line = String.Empty;
                int i = 0;
                while ((line = await sr.ReadLineAsync()) != null)
                {
                    if (!String.IsNullOrEmpty(line))
                    {
                        i++;
                        MatchCollection matches3 = Regex.Matches(line, "а");
                        count += matches3.Count;
                        Console.WriteLine($"in line num {i} count symbol a equal: {matches3.Count}");
                    }
                }
            }
            return count;
        }

        public static int CountOfThreads { get; set; } = 0;

        public static void ReadFileThread(string path)
        {
            StreamReader sr = File.OpenText(path);
            string line = String.Empty;
            var threads = new List<Thread>();
            while ((line = sr.ReadLine()) != null)
            {
                if (!String.IsNullOrEmpty(line))
                {
                    Thread thread = new Thread(new ParameterizedThreadStart(WorkWithLines));
                    thread.Start(line);
                    threads.Add(thread);
                }
            }
            foreach (var thread in threads)
            {
                thread.Join();
            }
        }

        public static int Counter { get; set; } = 0;


        private static void WorkWithLines(object line)
        {
            Counter++;
            MatchCollection matches3 = Regex.Matches(line.ToString(), "а");
            Console.WriteLine($"in line num {Counter} count symbol a equal: {matches3.Count}");

            lock (locker)
            {
                CountForThirdMethod += matches3.Count;
            }

        }

        static void Extract(string whereFile, string nameOfFolder, string namOfFile)
        {
            string nameOfFolder1 = "Resources";
            string namOfFile1 = "text.txt";

            Assembly assembly = Assembly.GetCallingAssembly();
            using (Stream s = assembly.GetManifestResourceStream("ConsoleApp1" + "." + (nameOfFolder1 == "" ? "" : nameOfFolder1 + ".") + namOfFile1))
            {
                using (BinaryReader r = new BinaryReader(s))
                {
                    using (FileStream fs = new FileStream(whereFile + "\\" + namOfFile, FileMode.OpenOrCreate))
                    {
                        using (BinaryWriter w = new BinaryWriter(fs))
                        {
                            w.Write(r.ReadBytes((int)s.Length));
                        }
                    }
                }

            }
        }

        static void PutFileInOutput()
        {
            Extract(Directory.GetCurrentDirectory(), "Resources", "text.txt");
        }

        static async Task Main(string[] args)
        {
            PutFileInOutput();
            string path = Directory.GetCurrentDirectory() + "\\text.txt";
            Stopwatch stopwatch = new Stopwatch();
            Stopwatch stopwatch2 = new Stopwatch();
            Stopwatch stopwatch3 = new Stopwatch();

            stopwatch.Start();
            ReadFile(path);
            int count1 = ReadFile(path);
            stopwatch.Stop();

            stopwatch2.Start();
            int count2 = await ReadFileAsync(path);
            stopwatch2.Stop();


            stopwatch3.Start();

            ReadFileThread(path);
            int count3 = CountForThirdMethod;
            stopwatch3.Stop();


            Console.WriteLine($"Wasted the time consistently method {stopwatch.Elapsed + Environment.NewLine}");

            Console.WriteLine($"Wasted the time async method {stopwatch2.Elapsed + Environment.NewLine}");

            Console.WriteLine($"Wasted the time multy Thread method {stopwatch3.Elapsed + Environment.NewLine}");

            Console.WriteLine($"Total count of \"a\" in consistently method: {count1 + Environment.NewLine}");

            Console.WriteLine($"Total count of \"a\" in async method: {count2 + Environment.NewLine}");

            Console.WriteLine($"Total count of \"a\" in multy Thread method: {count3 + Environment.NewLine}");

            Console.ReadKey();

Ответы (1 шт):

Автор решения: tym32167

Файл - это такая вещь, которая читается одним потоком (тут под потоком я подразумеваю Stream). То есть самое эффективное, что можно сделать - это читать файл порциями, с буфером, чем больше буфер и меньше чтений - тем лучше.

Ваш синхронный вариант просто читает файл без перерыва.

Ваш многопоточный вариант = это синхронный + вы создаете потоки (что является дорогой операцией) + вы потоки пытаетесь синхронизировать (что по сути тормозит выполнение). Потому этот ваш вариант медленней синхронного.

Ваш асинхронный вариант = это как синхронный + машина состояний. Потому этот вариант медленней, чем синхронный.

Таким образом, вы свой код могли даже не запускать, там и так ясно, кто быстрее обработает файл. Но сам пример не самый удачный. А теперь возьмите 300 жестких дисков и по 100 файлов на каждом и повторите эксперимент :)

В вашем случае узкое место - это файл, а не способ, как вы его обрабатываете.

Ещё чуть чуть полезной инфы.

→ Ссылка