Gerenciamento de Threads com ThreadPool em .Net Core e C#

0

A criação e a destruição de threads têm um custo alto e afetam o desempenho do aplicativo, mesmo que em pequenas proporções, isso seja invisível aos olhos do desenvolvedor. As threads podem, portanto, ser bloqueados ou entrar em repouso ou em outros estados não resolvidos, dificultando ainda mais o seu gerenciamento.

Se o seu aplicativo não distribuir a carga de trabalho corretamente, os threads de trabalho poderão passar a maior parte do tempo dormindo ou em fila e isto é algo que normalmente, não queremos. É aqui que o pool de threads é útil!

Um conjunto de threads ou ThreadPool é um conjunto de pool de threads que já foram criados e estão disponíveis para uso conforme necessário. Como se fosse uma “sacola cheia de threads”, prontas para executarem.

Depois que os threads do ThreadPool concluem a execução de suas tarefas, eles retornam ao pool. O .NET Core fornece um conjunto de threads gerenciados por meio da classe ThreadPool que é gerenciada pelo sistema. Como desenvolvedor, não precisamos lidar com a sobrecarga disso, já que este recurso contempla toda o gerenciamento e balanceamento das threads ali contidas. Lembrando que, threads criadas pelo ThreadPool são boas apenas para processos em segundo plano e não são recomendados para encadeamentos em primeiro plano. Existe apenas um ThreadPoll por processo.

O uso do pool de threads não é recomendado quando:

  • Você precisa priorizar uma thread.
  • A thread é um segmento em primeiro plano.
  • Você tem tarefas que fazem com que a thread bloqueie por longos períodos de tempo.
  • Você precisa colocar em modo single-threaded. Todos os threads ThreadPool são multithread.
  • Você precisa ter uma identidade estável associada à thread que está no ThreadPool

Se você é novo no segmento, comece com Threads aqui. A classe ThreadPool tem vários métodos estáticos, incluindo o QueueUserWorkItem que é responsável por chamar um thread de trabalho do pool de threads quando ele estiver disponível. Se nenhuma thread estiver disponível no pool, ele aguardará até que se torne disponível.
Aqui está um exemplo completo de como chamar um thread de trabalho do thread para executar um método em segundo plano.

    using System;  
    using System.Threading;  
      
    class ThreadPoolSample  
    {  
        // Background task   
        static void BackgroundTask(Object stateInfo)  
        {  
            Console.WriteLine("Hello! I'm a worker from ThreadPool");  
            Thread.Sleep(1000);          
        }  
      
        static void Main(string[] args)  
        {  
            // Use ThreadPool for a worker thread        
            ThreadPool.QueueUserWorkItem(BackgroundTask);  
            Console.WriteLine("Main thread does some work, then sleeps.");  
            Thread.Sleep(500);  
            Console.WriteLine("Main thread exits.");  
            Console.ReadKey();  
        }  
    }  


Você pode passar valores de um objeto ou o objeto completo para um método de backgroupd por meio do método QueueWorkItem. O segundo parâmetro do método é o objeto que pode ser qualquer objeto que você gostaria de passar para o seu procedimento em segundo plano.
Vamos supor que temos uma pessoa com os seguintes membros.

Você pode passar valores de um objeto ou o objeto completo para um método de backgroupd por meio do método QueueWorkItem. O segundo parâmetro do método é o objeto que pode ser qualquer objeto que você gostaria de passar para o seu procedimento em segundo plano.
Vamos supor que temos uma pessoa com os seguintes membros.

 
    // Create a Person class  
    public class Person  
    {  
        public string Name { get; set; }  
        public int Age { get; set; }  
        public string Sex { get; set; }  
      
        public Person(string name, int age, string sex)  
        {  
            this.Name = name;  
            this.Age = age;  
            this.Sex = sex;  
        }  
    }  

 
    // Create an object and pass it to ThreadPool worker thread  
    Person p = new Person("Jhonathan Soares", 28, "Male");  
    ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  
  
    static void BackgroundTaskWithObject(Object stateInfo)  
    {  
        Person data = (Person)stateInfo;          
        Console.WriteLine($"Hi {data.Name} from ThreadPool.");  
        Thread.Sleep(1000);  
    }  


O código completo está listado no código a seguir.

 
    using System;  
    using System.Threading;  
      
    class ThreadPoolSample  
    {  
        // Background task   
        static void BackgroundTask(Object stateInfo)  
        {  
            Console.WriteLine("Hello! I'm a worker from ThreadPool");  
            Thread.Sleep(1000);          
        }  
      
        static void BackgroundTaskWithObject(Object stateInfo)  
        {  
            Person data = (Person)stateInfo;          
            Console.WriteLine($"Hi {data.Name} from ThreadPool.");  
            Thread.Sleep(1000);  
        }  
        static void Main(string[] args)  
        {  
            // Create an object and pass it to ThreadPool worker thread  
            Person p = new Person("Mahesh Chand", 40, "Male");  
            ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  
      
            Console.ReadKey();  
        }  
      
            // Create a Person class  
            public class Person  
            {  
                public string Name { get; set; }  
                public int Age { get; set; }  
                public string Sex { get; set; }  
      
                public Person(string name, int age, string sex)  
                {  
                    this.Name = name;  
                    this.Age = age;  
                    this.Sex = sex;  
                }  
            }  
    }  


Threads de pool de threads máximo e mínimo

O tamanho do ThreadPool é definido pelo número de threads disponíveis nele. Por padrão, o número mínimo de threads é definido como o número de processadores disponíveis no sistema. Quando o mínimo é atingido, o conjunto de encadeamentos pode criar threads adicionais ou esperar até que algumas tarefas sejam concluídas. O thread pool cria e destrói threads para otimizar a performance num todo, que é definida como o número de tarefas que são concluídas em X tempo. O método ThreadPool.GetAvailalbeThreads retorna o número de segmentos que estão atualmente em um pool. Veja um exemplo de código completo com o segmento de código para visualizar a carga de trabalho disponível:

 
    using System;  
    using System.Threading;  
      
    class ThreadPoolSample  
    {  
        // Background task   
        static void BackgroundTask(Object stateInfo)  
        {  
            Console.WriteLine("Hello! I'm a worker from ThreadPool");  
            Thread.Sleep(1000);          
        }  
      
        static void BackgroundTaskWithObject(Object stateInfo)  
        {  
            Person data = (Person)stateInfo;          
            Console.WriteLine($"Hi {data.Name} from ThreadPool.");  
            Thread.Sleep(1000);  
        }  
        static void Main(string[] args)  
        {  
            // Use ThreadPool for a worker thread        
            ThreadPool.QueueUserWorkItem(BackgroundTask);  
            Console.WriteLine("Main thread does some work, then sleeps.");  
            Thread.Sleep(500);  
         
            // Create an object and pass it to ThreadPool worker thread  
            Person p = new Person("Jhonathan Soares", 28, "Male");  
            ThreadPool.QueueUserWorkItem(BackgroundTaskWithObject, p);  
      
            int workers, ports;  
      
            // Get maximum number of threads  
            ThreadPool.GetMaxThreads(out workers, out ports);  
            Console.WriteLine($"Maximum worker threads: {workers} ");  
            Console.WriteLine($"Maximum completion port threads: {ports}");  
          
            // Get available threads  
            ThreadPool.GetAvailableThreads(out workers, out ports);  
            Console.WriteLine($"Availalbe worker threads: {workers} ");  
            Console.WriteLine($"Available completion port threads: {ports}");  
      
            // Set minimum threads  
            int minWorker, minIOC;  
            ThreadPool.GetMinThreads(out minWorker, out minIOC);  
            ThreadPool.SetMinThreads(4, minIOC);  
      
            // Get total number of processes availalbe on the machine  
            int processCount = Environment.ProcessorCount;  
            Console.WriteLine($"No. of processes available on the system: {processCount}");  
      
            // Get minimum number of threads  
            ThreadPool.GetMinThreads(out workers, out ports);  
            Console.WriteLine($"Minimum worker threads: {workers} ");  
            Console.WriteLine($"Minimum completion port threads: {ports}");  
      
            Console.ReadKey();  
        }  
      
            // Create a Person class  
            public class Person  
            {  
                public string Name { get; set; }  
                public int Age { get; set; }  
                public string Sex { get; set; }  
      
                public Person(string name, int age, string sex)  
                {  
                    this.Name = name;  
                    this.Age = age;  
                    this.Sex = sex;  
                }  
            }  
    }  


Bom, por hoje é só! Um grande abraço a todos!

Referências: https://docs.microsoft.com/pt-br/dotnet/api/system.threading.threadpool?view=netcore-2.2

Compartilhe.

Sobre o autor

Criador do blog Código Simples e com mais 9 anos de experiência em TI, com títulos de MVP Microsoft na área de Visual Studio Development, Neo4j Top 50 Certificate, Scrum Master e MongoDB Evangelist. Atuando em funções analista, desenvolvedor, arquiteto, líder técnico e gestor de equipes. Mais informações em : http://jhonathansoares.com