Потоки не создаются правильно. Очень просто WaitAny c # - PullRequest
0 голосов
/ 09 июля 2019

У меня есть очень простой код, который пытается многопоточность существующего сценария.

При проверке окна протекторов в Visual Studio и вызове Thread.CurrentThread.ManagedThreadId оно всегда сообщает о том же потоке, что и при запуске процесса.По окончании он сообщает другой идентификатор потока.

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

Пожалуйста, кто-нибудь может уточнить, что происходит, и если я ошибся в своем подходе?

namespace ResolveGoogleURLs
{
    class Program
    {
        public static void Main(string[] args)
        {
            HomeController oHC = new HomeController();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace ResolveGoogleURLs
{
    class HomeController
    {
        public static int MaxJobs = 5;
        public static int RecordsPerJob = 1000;

        public static List<Task> TaskList = new List<Task>(MaxJobs);


        public HomeController()
        {
            CreateJobs();

            MonitorTasks();
        }


        public void MonitorTasks()
        {
            while (1 == 1)
            {
                Task.WaitAny(TaskList.ToArray());

                TaskList.RemoveAll(x => x.IsCompleted);

                Console.WriteLine("Task complete! Launching new...");
                CreateJobs();
            }
        }


        public async Task CreateJob()
        {
            Console.WriteLine("Thread {0} - Start", Thread.CurrentThread.ManagedThreadId);

            // read in results from sql

            await Task.Delay(10000);
            Console.WriteLine("Thread {0} - End", Thread.CurrentThread.ManagedThreadId);
        }


        public void CreateJobs()
        {
            while (TaskList.Count < MaxJobs)
            {
                TaskList.Add( CreateJob() );
            }
        }
    }
}

Выход:

> Thread 1 - Start
Thread 1 - Start
Thread 1 - Start
Thread 1 - Start
Thread 1 - Start
Thread 4 - End
Thread 5 - End
Thread 4 - End
Thread 6 - End
Thread 8 - End
Task complete! Launching new...
Thread 1 - Start
Thread 1 - Start
Thread 1 - Start
Thread 1 - Start
Thread 1 - Start
Thread 7 - End
Thread 6 - End
Thread 5 - End
Thread 4 - End
Thread 8 - End
Task complete! Launching new...
Thread 1 - Start
Thread 1 - Start
Thread 1 - Start
Task complete! Launching new...
Thread 1 - Start
Thread 1 - Start
Thread 10 - End
Thread 9 - End
Task complete! Launching new...
Thread 1 - Start
Thread 7 - End
Thread 4 - End
Thread 6 - End
Task complete! Launching new...
Thread 1 - Start
Thread 1 - Start
Task complete! Launching new...
Thread 1 - Start
Thread 1 - Start

1 Ответ

0 голосов
/ 09 июля 2019

Задачи (class Task) не совпадают с потоками (class Thread).

Поток можно представить как виртуальный ЦП, который может выполнять свой код в то же время, что и другие потоки.Каждый поток имеет свой собственный стек (где хранятся локальные переменные и параметры функции).В .NET поток отображается в собственный поток, поддерживаемый платформой (операционной системой), которая имеет такие вещи, как объект ядра потока, стек режима ядра, блок выполнения потока и т. Д. Это делает поток довольно тяжелым объектом.Создание нового потока занимает много времени, и даже когда поток спит (не выполняет его код), он все равно потребляет много памяти (стек потоков).

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

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

Задача внутренне использует потоки, потому что потоки являются единственным способом параллельного запуска кода в .NET.(На самом деле это единственный способ запустить любой код.) Но он делает это эффективным способом.

Когда запускается .NET-процесс, внутренне он создает пул потоков - пул потоков, которые используются длявыполнять задачи.

Библиотека задач реализована таким образом, что при запуске процесса пул потоков содержит только один поток.Если вы начинаете создавать новые задачи, они сохраняются в очереди, из которой этот отдельный поток получает и выполняет одну задачу за другой.Но .NET контролирует, если этот поток не перегружен слишком большим количеством задач - ситуация, когда задачи ожидают «слишком долго» в очереди пула потоков.Если - на основании своих внутренних критериев - он обнаруживает, что изначально созданный поток перегружен, он создает новый.Таким образом, пул потоков теперь имеет 2 потока, и задачи могут выполняться на 2 из них параллельно.Этот процесс можно повторить, поэтому при наличии большой нагрузки в пуле потоков может быть 3, 4 или более потоков.

С другой стороны, если частота новых задач падает, а потоки пула потоков не работаютУ него нет задач для выполнения, после того, как «некоторое время» (то, что «некоторое время» определяется критериями внутреннего пула потоков), пул потоков может решить освободить некоторые потоки - для сохранения системных ресурсов.Количество потоков пула потоков может уменьшиться до первоначально созданного отдельного потока.

Таким образом, библиотека задач внутренне использует потоки, но пытается использовать их эффективно.Количество потоков увеличивается и уменьшается в зависимости от количества задач, которые программа хочет выполнить.

Практически во всех случаях задачи должны быть предпочтительным решением по сравнению с использованием «сырых» потоков.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...