.NET
Threading
INTRODUÇÃO A PROGRAMAÇÃO CONCORRENTE
Processos vs Threads
 Processos são containers mapeados para um endereço de memoria no
qual outros processos não tem permissão de leitura
Memoria Processos
Threads
 Roda todo ou parte do código dentro de um
processo
 Tem acesso a um pedaço ou toda memoria
mapeada para o processo
 Toda thread tem sua propina copia da
callstack e registradores da CPU.
 Um processo sem nenhuma thread é
finalizado, já que não esta realizando nenhum
execução.
Vantagens do uso de multi-threading
 Possibilidade de escalar as operações da CPU a partir de paralelização de
processamento de dados (assumindo um hardware com multi-core/multi-
processadores)
 Realizar operações variadas enquanto esperamos por operações de I/O
finalizem.
 Manter uma UI responsiva
Preocupações com muti-threading
 Adiciona complexidade ao programa
 Linhas de código
 Leitura
 Testabilidade
 Execução mais lenta em maquinas com
um core ou processador
Como iniciar um thread?
Como iniciar thread com parâmetros
Threads com métodos de instancia
Tempo de vida de uma thread
 Uma thread para quando
 O método da thread retorna
 Ocorre um exception não tratada (synchronous exception)
 Outra thread para ela com “Interrupt”ou “Abort” (asynchronous exception)
A propriedade IsAlive prove um snapshot instantaneo do status da thread
IsAlive == false
Como parar uma thread?
Idealmente é ter uma logica pré-definida para abortar a execução
Thread-pool
 Prove uma forma em que as threads são
emprestadas para operações
concorrentes que sejam rápidas.
 CLR prove uma thread-pool por
processo.
 Threads são adicionadas ou removidas
de acordo com a demanda
 Permite que o custo de criar e matar
threads seja reduzido durante a vida do
processo
 Por padrão tem a propriedade
IsBackground definida como `true`
 Usado de implicitamente forma padrão
ao utilizar Async I/O, Delegate.Invoke,
Parallel.For, PLINQ
Parallel.For e PLINQ
Usam por padrão o threadpool
ThreadLocal State
“System.NotSupportedException -> WebClient does not support concurrent I/O operations.”
ThreadLocal State
Com isso seu programa instancia mais de 100 WebClients, seu programa lança uma exceção que
informa que seus webclients estão dando timeout. Você percebe que é porque sua máquina não
está executando um sistema operacional de servidor, e há um limite máximo no número de
conexões simultâneas.
ThreadLocal State
Nesse caso, cada operação de acesso a dados é inteiramente independente um do outro.
O uso do thread-local state nos permitiu garantir que gerássemos apenas tantos objetos
WebClient quanto necessário, e que cada WebClient pertence a thread que o gerou.
ThreadLocal State
O mesmo é conseguido com PLINQ utilizando o ThreadLocal<>, porém é importante ressaltar
que, em qualquer cenário, usando o tipo ThreadLocal<> é mais custoso do que usar o overload
de ThreadLocalState do Parallel.ForEach
Sincronização de threads
 A maior parte dos recursos dentro de um programa não são pensados
para serem usados de forma concorrente.
 Collections (array, list, dictionary, etc)
 Files
 Mesmo inteiros
Oque deveria ser exibido?
Oque será exibido?
Sessões criticas
 Uma sessão critica é uma região do código que ira acessar um recurso
compartilhado:
Race
Condition
Solução 1: Atomic updates
 Maior parte dos processadores suportam atomic updates
Solução 2: Particionamento
Solução 3: Wait Based Synchronization
 Quando threads necessitam de acessar o mesmo recurso que não pode ser
particionado (adicionar ou remover um no de uma lista, ou manipular o
mesmo arquivo)
 Algumas vezes queremos que uma thread esteja block ate algum evento
ocorrer.
 A casos que os dados dependem de um passo anterior inviabilizando
particionamento.
 Quando o input de uma thread depende do output de outra
 Calcular a sequencia de Fibonacci
 Sequence[0] = Sequence[1] = 1
 Sequence[n] = Sequence[n-1]+Sequence[n-2]
Wait-Based Thread Synchronization
Wait-Based Thread Synchronization
Primitives
 Existem vários primitivos para controle de threads no CLR
System.Threading:
 Monitor
 Mutex
 ReaderWriterLockSlim
 ManualResetEvent, AutoResetEvent
 Semaphore, SlimSemaphore
 Os 3 primeiros compartilham o mesmo modelo de uso:
 Faz uma chamada para adquirir o owner do “lock”
 Usa o recurso compartilhado que o “lock” designado visa proteger
 Faz um chamada para liberar o “lock” assim que o recurso não é mais
necessário
System.Threading.Monitor
 Monitor garante acesso exclusivo a um recurso
 CLR permite apenas uma thread entrar no contexto do
monitor por vez
 Outras threads que tentarem entrar irão ficar bloqueada
 Assim que o recurso for liberado a próxima thread na
espera ira adquirir para si o acesso exclusivo ao recurso
Monitors no CLR
 Metodos do monitor operam em cima de uma referencia de um objeto
 Qualquer referencia de objeto no HEAP pode potencialmente ser associado a
um lock
Monitor: Tratar exceptions
C# Monitor syntax sugar
Hold & Wait
 As vezes uma thread precisa e esperar alguma coisa enquanto segura um
lock
 Em scenarios de producer/consumer é comum ter que lidar com
gerenciamento de disponibilidade de recursos.
 Mutipla aquisição de locks
Hold & Wait com Monitors
ManualResetEvent
 Controle de fluxo
 Precisa ser
explicitamente
resetado
AutoResetEvent
 Controle de fluxo
 É resetado a cada
WaitOne
DEADLOCK
DEADLOCK
 Deadlocks podem ocorrer a qualquer momento em que esteja numa
situação de Hold & Wait
 Enquento segura um lock a thread tenta obter outro lock
Deadlocks podem ocorrer mas não necessariamente vão ocorrer
Deadlocks são possíveis mas não necessariamente prováveis (a probabilidade
aumenta de acordo com o # de threads/processadores/cores)
Deadlock podem talvez ser temporário caso seja utilizado timeouts nas aquisições
de lock
COMO ACONTECE?
Mutexes
 Um Mutex é um objeto de Kernel (Sytem.Threading.Mutex)
 Suporta timeout em aquisições de lock
 Nomeável, permite sincronização cross-process na mesma maquina
 Habilita o uso múltipla aquisições de lock com de deadlock-free via
WaitHandle.WaitAll
 Tradeoffs
 Chamadas de aquisições e liberação de locks sempre ocorrem em kernel mode
 Objetos de kernel devem ser fechados assim que não mais necessários (ocorre
automaticamente mas depende do GC)
Código propicio a deadlock
Sem deadlock
Semaphore
 Funciona de forma similar ao Monitor ou Mutex, porem ele delimita
quantas threads podem acessar uma sessão critica ao mesmo tempo.
 É como uma balada como uma fila, aonde as pessoas na fila esperam uma
pessoa sair da balada para entrar.
 System.Threading.SemaphoreSlim é mais leve, não faz chamdas de kernel, para
ser usado em apenas um processo.
 System.Threading.Semaphore pode ser nomeado e funciona entre processos
 Não tem garantia de ordem
Semaphore
ReaderWriterLockSlim
 Possui trem modos : Read, Write e Ungradeable
 Muitas threads podem estar simutaneamente em modo Read
 Apenas uma pode entrar em modo Ungradeable mas outras threads
podem entrar em modo read
 Apenas uma Thread pode entrar em modo write, e nenhuma outra thread
pode adquirir um lock
Concurrent Collections
Immutable Collections
 NuGet System.Collection.Immutable
 Thread Safe por definição
Keyword : volatile
 Não reordena instruções na memoria
 “Garante” que a leitura do valor mais novo será realizada
 Resolvia o problema de double null check em versões do .NET < 2
 Deve ser evitado
 Apenas utilizado em casos em que o cenario de locks não esta atendendo
a performance desejada
E o TPL? Async await?
 IMPORTANTE: Assíncrono não é equivalente a paralelo.
 A classe Task ou ValueTask não necessariamente estão vinculadas com
threads.
 O Método estático Task.Run() ou Task.Factory.StartNew() sempre inicia
uma thread, e é a forma recomendada para se criar threads no .NET
moderno.
 Mais sobre isso na proxima, sobre .NET Assincrono

Net - Threads

  • 1.
  • 2.
    Processos vs Threads Processos são containers mapeados para um endereço de memoria no qual outros processos não tem permissão de leitura Memoria Processos
  • 3.
    Threads  Roda todoou parte do código dentro de um processo  Tem acesso a um pedaço ou toda memoria mapeada para o processo  Toda thread tem sua propina copia da callstack e registradores da CPU.  Um processo sem nenhuma thread é finalizado, já que não esta realizando nenhum execução.
  • 4.
    Vantagens do usode multi-threading  Possibilidade de escalar as operações da CPU a partir de paralelização de processamento de dados (assumindo um hardware com multi-core/multi- processadores)  Realizar operações variadas enquanto esperamos por operações de I/O finalizem.  Manter uma UI responsiva
  • 5.
    Preocupações com muti-threading Adiciona complexidade ao programa  Linhas de código  Leitura  Testabilidade  Execução mais lenta em maquinas com um core ou processador
  • 6.
  • 7.
    Como iniciar threadcom parâmetros
  • 8.
  • 9.
    Tempo de vidade uma thread  Uma thread para quando  O método da thread retorna  Ocorre um exception não tratada (synchronous exception)  Outra thread para ela com “Interrupt”ou “Abort” (asynchronous exception) A propriedade IsAlive prove um snapshot instantaneo do status da thread IsAlive == false
  • 10.
    Como parar umathread? Idealmente é ter uma logica pré-definida para abortar a execução
  • 11.
    Thread-pool  Prove umaforma em que as threads são emprestadas para operações concorrentes que sejam rápidas.  CLR prove uma thread-pool por processo.  Threads são adicionadas ou removidas de acordo com a demanda  Permite que o custo de criar e matar threads seja reduzido durante a vida do processo  Por padrão tem a propriedade IsBackground definida como `true`  Usado de implicitamente forma padrão ao utilizar Async I/O, Delegate.Invoke, Parallel.For, PLINQ
  • 12.
    Parallel.For e PLINQ Usampor padrão o threadpool
  • 13.
    ThreadLocal State “System.NotSupportedException ->WebClient does not support concurrent I/O operations.”
  • 14.
    ThreadLocal State Com issoseu programa instancia mais de 100 WebClients, seu programa lança uma exceção que informa que seus webclients estão dando timeout. Você percebe que é porque sua máquina não está executando um sistema operacional de servidor, e há um limite máximo no número de conexões simultâneas.
  • 15.
    ThreadLocal State Nesse caso,cada operação de acesso a dados é inteiramente independente um do outro. O uso do thread-local state nos permitiu garantir que gerássemos apenas tantos objetos WebClient quanto necessário, e que cada WebClient pertence a thread que o gerou.
  • 16.
    ThreadLocal State O mesmoé conseguido com PLINQ utilizando o ThreadLocal<>, porém é importante ressaltar que, em qualquer cenário, usando o tipo ThreadLocal<> é mais custoso do que usar o overload de ThreadLocalState do Parallel.ForEach
  • 17.
    Sincronização de threads A maior parte dos recursos dentro de um programa não são pensados para serem usados de forma concorrente.  Collections (array, list, dictionary, etc)  Files  Mesmo inteiros Oque deveria ser exibido? Oque será exibido?
  • 18.
    Sessões criticas  Umasessão critica é uma região do código que ira acessar um recurso compartilhado:
  • 19.
  • 20.
    Solução 1: Atomicupdates  Maior parte dos processadores suportam atomic updates
  • 21.
  • 22.
    Solução 3: WaitBased Synchronization  Quando threads necessitam de acessar o mesmo recurso que não pode ser particionado (adicionar ou remover um no de uma lista, ou manipular o mesmo arquivo)  Algumas vezes queremos que uma thread esteja block ate algum evento ocorrer.  A casos que os dados dependem de um passo anterior inviabilizando particionamento.  Quando o input de uma thread depende do output de outra  Calcular a sequencia de Fibonacci  Sequence[0] = Sequence[1] = 1  Sequence[n] = Sequence[n-1]+Sequence[n-2]
  • 23.
  • 24.
    Wait-Based Thread Synchronization Primitives Existem vários primitivos para controle de threads no CLR System.Threading:  Monitor  Mutex  ReaderWriterLockSlim  ManualResetEvent, AutoResetEvent  Semaphore, SlimSemaphore  Os 3 primeiros compartilham o mesmo modelo de uso:  Faz uma chamada para adquirir o owner do “lock”  Usa o recurso compartilhado que o “lock” designado visa proteger  Faz um chamada para liberar o “lock” assim que o recurso não é mais necessário
  • 25.
    System.Threading.Monitor  Monitor garanteacesso exclusivo a um recurso  CLR permite apenas uma thread entrar no contexto do monitor por vez  Outras threads que tentarem entrar irão ficar bloqueada  Assim que o recurso for liberado a próxima thread na espera ira adquirir para si o acesso exclusivo ao recurso
  • 26.
    Monitors no CLR Metodos do monitor operam em cima de uma referencia de um objeto  Qualquer referencia de objeto no HEAP pode potencialmente ser associado a um lock
  • 27.
  • 28.
  • 29.
    Hold & Wait As vezes uma thread precisa e esperar alguma coisa enquanto segura um lock  Em scenarios de producer/consumer é comum ter que lidar com gerenciamento de disponibilidade de recursos.  Mutipla aquisição de locks
  • 30.
    Hold & Waitcom Monitors
  • 31.
    ManualResetEvent  Controle defluxo  Precisa ser explicitamente resetado
  • 32.
    AutoResetEvent  Controle defluxo  É resetado a cada WaitOne
  • 33.
  • 34.
    DEADLOCK  Deadlocks podemocorrer a qualquer momento em que esteja numa situação de Hold & Wait  Enquento segura um lock a thread tenta obter outro lock Deadlocks podem ocorrer mas não necessariamente vão ocorrer Deadlocks são possíveis mas não necessariamente prováveis (a probabilidade aumenta de acordo com o # de threads/processadores/cores) Deadlock podem talvez ser temporário caso seja utilizado timeouts nas aquisições de lock
  • 35.
  • 36.
    Mutexes  Um Mutexé um objeto de Kernel (Sytem.Threading.Mutex)  Suporta timeout em aquisições de lock  Nomeável, permite sincronização cross-process na mesma maquina  Habilita o uso múltipla aquisições de lock com de deadlock-free via WaitHandle.WaitAll  Tradeoffs  Chamadas de aquisições e liberação de locks sempre ocorrem em kernel mode  Objetos de kernel devem ser fechados assim que não mais necessários (ocorre automaticamente mas depende do GC)
  • 37.
  • 38.
  • 39.
    Semaphore  Funciona deforma similar ao Monitor ou Mutex, porem ele delimita quantas threads podem acessar uma sessão critica ao mesmo tempo.  É como uma balada como uma fila, aonde as pessoas na fila esperam uma pessoa sair da balada para entrar.  System.Threading.SemaphoreSlim é mais leve, não faz chamdas de kernel, para ser usado em apenas um processo.  System.Threading.Semaphore pode ser nomeado e funciona entre processos  Não tem garantia de ordem
  • 40.
  • 41.
    ReaderWriterLockSlim  Possui tremmodos : Read, Write e Ungradeable  Muitas threads podem estar simutaneamente em modo Read  Apenas uma pode entrar em modo Ungradeable mas outras threads podem entrar em modo read  Apenas uma Thread pode entrar em modo write, e nenhuma outra thread pode adquirir um lock
  • 42.
  • 43.
    Immutable Collections  NuGetSystem.Collection.Immutable  Thread Safe por definição
  • 44.
    Keyword : volatile Não reordena instruções na memoria  “Garante” que a leitura do valor mais novo será realizada  Resolvia o problema de double null check em versões do .NET < 2  Deve ser evitado  Apenas utilizado em casos em que o cenario de locks não esta atendendo a performance desejada
  • 45.
    E o TPL?Async await?  IMPORTANTE: Assíncrono não é equivalente a paralelo.  A classe Task ou ValueTask não necessariamente estão vinculadas com threads.  O Método estático Task.Run() ou Task.Factory.StartNew() sempre inicia uma thread, e é a forma recomendada para se criar threads no .NET moderno.  Mais sobre isso na proxima, sobre .NET Assincrono