haskell concorrente - mcta016-13 - paradigmas de...

65
Haskell Concorrente MCTA016-13 - Paradigmas de Programação Emilio Francesquini [email protected] 2019.Q2 Centro de Matemática, Computação e Cognição Universidade Federal do ABC

Upload: others

Post on 04-Aug-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Haskell ConcorrenteMCTA016-13 - Paradigmas de Programação

Emilio [email protected]

Centro de Matemática, Computação e CogniçãoUniversidade Federal do ABC

Page 2: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Disclaimer

■ Estes slides foram preparados para o curso de Paradigmasde Programação na UFABC.

■ Este material pode ser usado livremente desde que sejammantidos, além deste aviso, os créditos aos autores einstituições.

■ Conteúdo baseado no texto preparado, e gentilmentecedido, pelo Professor Fabrício Olivetti de França daUFABC.

1

Page 3: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Haskell Concorrente

Page 4: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Programação Concorrente

O objetivo da programação concorrente é definirexplicitamente múltiplos caminhos de controle, geralmentepara permitir múltiplas interações com usuário ou interfacesexternas.

Não necessariamente serão executadas em paralelo.

2

Page 5: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Programação Concorrente

A ideia é descrever cada interação em separado, mas fazercom que elas ocorram ao mesmo tempo (intercaladamente).

3

Page 6: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Programação Concorrente

Considere um Web Browser que permite carregar múltiplaspáginas ao mesmo tempo.

Além disso, enquanto ele carrega uma ou mais páginas, ainterface ainda interage com o usuário (apertar o botãocancelar, etc.)

4

Page 7: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Programação Concorrente

Um servidor Web também implementa concorrência paraservir múltiplos pedidos ao mesmo tempo.

Imagine a situação de um servidor Web atendendo um pedidode página por vez.

5

Page 8: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Haskell Concorrente

O Haskell fornece um conjunto de funcionalidades simplesmas genéricas que podem ser utilizadas para definir estruturasmais complexas.

Com isso fica a cargo do programador definir o melhor modelode programação concorrente.

6

Page 9: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Conceitos Básicos

Page 10: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Criando uma thread

Uma thread é a menor sequência de computação que pode sergerenciada de forma independente pelo gerenciador detarefas.

Um processo é um procedimento computacional completo quepode conter diversas threads de execução.

7

Page 11: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Criando uma thread

Múltiplas threads de um mesmo processo compartilham amemória alocada, podendo utilizá-la para troca de mensagens.

Múltiplos processos não compartilham memória alocada.

8

Page 12: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Criando uma thread

No Haskell criamos uma thread com a função forkIO:

1 import Control.Concurrent2

3 forkIO :: IO () -> IO ThreadId

Ela recebe uma ação computacional de IO como argumento eretorna um identificador dessa thread.

9

Page 13: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Criando uma thread

A ideia é que todo efeito colateral feito pela ação IO será feitode forma concorrente com outras threads:

1 import Control.Concurrent2 import Control.Monad3 import System.IO4

5 main = do6 hSetBuffering stdout NoBuffering7 forkIO (replicateM_ 100000 (putChar 'A'))8 replicateM_ 100000 (putChar 'B')

10

Page 14: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Criando uma thread

A primeira linha da função main desativa o buffer paraexecutar toda ação IO no momento exato em que ela éenviada.

1 import Control.Concurrent2 import Control.Monad3 import System.IO4

5 main = do6 hSetBuffering stdout NoBuffering7 forkIO (replicateM_ 100000 (putChar 'A'))8 replicateM_ 100000 (putChar 'B')

11

Page 15: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Criando uma thread

A segunda linha cria uma thread que imprimirá o caractere Adez mil vezes.

1 import Control.Concurrent2 import Control.Monad3 import System.IO4

5 main = do6 hSetBuffering stdout NoBuffering7 forkIO (replicateM_ 100000 (putChar 'A'))8 replicateM_ 100000 (putChar 'B')

12

Page 16: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Criando uma thread

A terceira linha imprime o caractere B dez mil vezes.

1 import Control.Concurrent2 import Control.Monad3 import System.IO4

5 main = do6 hSetBuffering stdout NoBuffering7 forkIO (replicateM_ 100000 (putChar 'A'))8 replicateM_ 100000 (putChar 'B')

13

Page 17: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Criando uma thread

A execução do programa resultará em caracteres A e Bintercalados:

AAAAAAAAABABABABABABABABABABABABAABABABABAB…

14

Page 18: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Exemplo 1: Lembretes

Vamos criar uma função multithread que aguarda o usuárioentrar com um tempo em segundos e cria uma thread paraimprimir uma mensagem após esse número de segundostenha passado:

1 import Control.Concurrent2 import Control.Monad3

4 main = forever $ do5 s <- getLine6 forkIO $ setReminder s

15

Page 19: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Exemplo 1: Lembretes

A função forever está definida na bibliotecaControl.Monad e simplesmente repete a ação eternamente.A função setReminder pode ser definida como:

1 setReminder :: String -> IO ()2 setReminder s = do3 let t = read s :: Int4 putStrLn ("Ok, adicionado para " ++ show t ++ "

segs.")↪→

5 threadDelay (10^6 * t)6 putStrLn (show t ++ " segundos se passaram! BING!"

A função threadDelay suspende a execução da thread por nmicrosegundos.

16

Page 20: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Exemplo 1: Lembretes

Exemplo de execução:

1 $ ./lembrete2 23 Ok, adicionado para 2 segs.4 45 Ok, adicionado para 4 segs.6 2 segundos se passaram! BING!7 4 segundos se passaram! BING!

17

Page 21: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Exercício 01 (0.5 pto)

Altere o programa anterior para terminar o programa assimque o usuário digitar quit.

DICA: troque o uso de forever por uma função definida porvocê denominada repita.

18

Page 22: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Resposta

1 main = repita2 where repita = do3 s <- getLine4 if s == "quit"5 then return ()6 else do forkIO $ setReminder s7 repita

19

Page 23: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Comunicação entre threads: MVar

Para que as threads possam se comunicar entre si é necessárioa existência de um espaço de memória compartilhada.

No Haskell isso é implementado através do tipo MVar:

1 data MVar a2

3 newEmptyMVar :: IO (MVar a)4 newMVar :: a -> IO (MVar a)5 takeMVar :: MVar a -> IO a6 putMVar :: MVar a -> a -> IO ()

20

Page 24: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

MVar

O tipo MVar é um container para qualquer tipo de dado. Vocêpode armazenar um valor Integer, uma String, uma listade Bool, etc.A função newEmptyMVar cria um MVar inicialmente vazio. Afunção newMVar recebe um argumento x e retorna um MVarcontendo x.As funções takeMVar e putMVar, inserem e removem umconteúdo em um MVar.

21

Page 25: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

MVar

Notem que MVar armazena apenas um único valor em suaestrutura:

■ Se uma thread chamar takeMVar e ela estiver vazia,ficará bloqueada em espera até que algum conteúdo sejainserido.

■ Se uma thread chamar putMVar e ela estiver cheia, ficarábloqueada em espera até que alguema thread utilizetakeMVar.

22

Page 26: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

MVar

Considere o seguinte código exemplo:

1 main = do2 m <- newEmptyMVar3 forkIO $ do putMVar m 'x'; putMVar m 'y'4 r1 <- takeMVar m5 print r16 r2 <- takeMVar m7 print r2

23

Page 27: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

MVar

Inicialmente, cria-se uma MVar vazia:

1 main = do2 m <- newEmptyMVar3 forkIO $ do putMVar m 'x'; putMVar m 'y'4 r1 <- takeMVar m5 print r16 r2 <- takeMVar m7 print r2

24

Page 28: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

MVar

Em seguida, criamos uma thread que armazena dois caracteresna sequência, ao inserir o primeiro caractere a thread ficabloqueada aguardando espaço ser liberado.

1 main = do2 m <- newEmptyMVar3 forkIO $ do putMVar m 'x'; putMVar m 'y'4 r1 <- takeMVar m5 print r16 r2 <- takeMVar m7 print r2

25

Page 29: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

MVar

Nesse momento o thread principal recupera o valor de MVar earmazena em r1, liberando espaço para a thread armazenar osegundo caractere.

1 main = do2 m <- newEmptyMVar3 forkIO $ do putMVar m 'x'; putMVar m 'y'4 r1 <- takeMVar m5 print r16 r2 <- takeMVar m7 print r2

26

Page 30: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

MVar

Ao final, o segundo caractere é recuperado e MVar se tornavazio.

1 main = do2 m <- newEmptyMVar3 forkIO $ do putMVar m 'x'; putMVar m 'y'4 r1 <- takeMVar m5 print r16 r2 <- takeMVar m7 print r2

27

Page 31: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Mecanismo anti-burrice

E se o programador escrever o seguinte programa:

1 main = do2 m <- newEmptyMVar3 takeMVar m

28

Page 32: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Mecanismo anti-burrice

A execução retornará:

1 $ ./burro2 burro: thread blocked indefinitely in an MVar operation

Se você criar um programa em que a thread fica bloqueadaeternamente, em muitos casos o programa emite uma exceçãoBlockedIndefinitelyOnMVar.

29

Page 33: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Construindo tipos mutáveis comMVar

Page 34: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Tipos mutáveis

Como sabemos, os tipos do Haskell são imutáveis, ou seja, umavez que definimos uma variável ela não pode mudar de valor.

O tipo MVar pode nos ajudar a simular um tipo mutável apartir de um tipo imutável de forma transparente!

30

Page 35: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Tipo Map

A biblioteca Data.Map fornece o tipo mapa associativo:

1 data Map k a2

3 import qualified Data.Map as M

que define um mapa associativo com chave do tipo k e valoresdo tipo a.

31

Page 36: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Tipo Map

Essa biblioteca possui as funções:

1 M.empty :: Map k a2

3 M.insert :: Ord k => k -> a -> Map k a -> Map k a4 M.insert k v m = -- insere valor v na chave k do mapa m,5 -- substituindo caso já exista6

7 M.lookup :: Ord k => k -> Map k a -> Maybe a8 M.lookup k m = -- retorna o valor na chave k do mapa m,9 -- retorna Nothing caso não exista a chave

32

Page 37: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Agenda telefônica

Vamos criar o tipo agenda telefônica:

1 type Nome = String2 type Numero = String3 type Agenda = M.Map Nome Numero

33

Page 38: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Agenda telefônica

Mas uma agenda telefônica não pode ser uma estruturaimutável. Preciso ter a capacidade de inserir novos nomes eatualizar as entradas. Vamos definir uma agenda telefônicamutável como:

1 newtype AgendaMut = AgendaMut (MVar Agenda)

34

Page 39: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Exercício (0.5 pto)

Defina a função novaAgenda que cria uma agenda mutávelvazia.

1 novaAgenda :: IO AgendaMut

35

Page 40: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Resposta

1 novaAgenda :: IO AgendaMut2 novaAgenda = do m <- newMVar M.empty3 return (AgendaMut m)

36

Page 41: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Exercício

Defina agora a função insere que insere um nome e umtelefone:

1 insere :: AgendaMut -> Nome -> Numero -> IO ()2 insere (AgendaMut m) nome numero = ??

37

Page 42: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Resposta

1 insere :: AgendaMut -> Nome -> Numero -> IO ()2 insere (AgendaMut m) nome numero = do3 agenda <- takeMVar m4 putMVar m (M.insert nome numero agenda)

38

Page 43: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Exercício

Defina a função procura que retorna uma entrada da agenda:

1 procura :: AgendaMut -> Nome -> IO (Maybe Numero)2 procura (AgendaMut m) -> Nome = do

39

Page 44: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Resposta

1 procura :: AgendaMut -> Nome -> IO (Maybe Numero)2 procura (AgendaMut m) nome = do3 agenda <- takeMVar m4 putMVar m agenda -- guarda de volta5 return (M.lookup nome agenda)

40

Page 45: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Agenda Mutável

Dessa forma, podemos trabalhar com a agenda da seguinteforma:

1 nomes = [("Joao", "111-222"), ("Maria", "222-111"),2 ("Marcos", "333-222")]3

4 main = do5 s <- novaAgenda6 mapM_ (uncurry (insere s)) nomes7 print =<< procura s "Marcos"8 print =<< procura s "Ana"

41

Page 46: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Agenda Mutável

O operador =<< é igual ao operador >>= mas invertido.Converta a expressão:

1 print =<< procura s "Marcos"

para a notação do

42

Page 47: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Resposta

1 do x <- procura s "Marcos"2 print x

43

Page 48: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Agenda Mutável

Se essas funções forem utilizadas em um programa que criamúltiplas threads podemos observar algumas vantagens:

■ Durante a operação de busca não é necessário criar umlock no estado da agenda durante a operação.

■ Graças a avaliação preguiçosa, também não se faznecessário um lock por muito tempo no momento dainserção.

44

Page 49: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

Page 50: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

Imagine a situação em que desejamos capturar o conteúdo deuma lista de páginas da Web. Queremos fazer isso de formaconcorrente e, após o encerramento de todas as threads,quero aplicar alguma função nos resultados.

45

Page 51: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas (1/3)

Nosso código seria algo como:

1 import Control.Concurrent2 import Data.ByteString as B3 import GetURL4

5 main = do6 m1 <- newEmptyMVar7 m2 <- newEmptyMVar

46

Page 52: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas (2/3)

1 forkIO $ do2 r <- getURL "http://www.wikipedia.org/wiki/Shovel"3 putMVar m1 r4

5 forkIO $ do6 r <- getURL "http://www.wikipedia.org/wiki/Spade"7 putMVar m2 r

47

Page 53: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas (3/3)

1 r1 <- takeMVar m12 r2 <- takeMVar m23 print (B.length r1, B.length r2)

48

Page 54: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

A ideia é que as duas threads façam o download do conteúdode cada URL em background assincronamente.

Uma operação assíncrona é uma operação que é feita embackground enquanto eu posso fazer outras operações quenão dependam dela, e permita que eu aguarde o final pararealizar alguma operação sobre os resultaos.

49

Page 55: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

O código está muito repetitivo. E se eu quiser fazer a mesmaoperação para uma lista de URLs? Vamos generalizar:

1 data Async a = Async (MVar a)

50

Page 56: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

1 async :: IO a -> IO (Async a)2 async action = do3 var <- newEmptyMVar4 forkIO (do r <- action; putMVar var r)5 return (Async var)

51

Page 57: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

1 wait :: Async a -> IO a2 wait (Async var) = readMVar var

Essas funções estão definidas na bibliotecaControl.Concurrent.Async

52

Page 58: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

A função async cria uma thread a ser executadaassíncronamente com outras.

A função wait aguarda o final da execução de uma threadassíncrona.

53

Page 59: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

Dessa forma nosso código pode ser reescrito como:

1 import Control.Concurrent2 import Data.ByteString as B3 import GetURL4

5 url1 = "http://www.wikipedia.org/wiki/Shovel"6 url2 = "http://www.wikipedia.org/wiki/Spade"7

8 main = do9 a1 <- async (getURL url1)

10 a2 <- async (getURL url2)11 r1 <- wait a112 r2 <- wait a213 print (B.length r1, B.length r2)

54

Page 60: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Operações Assíncronas

Assim, podemos capturar uma lista de sites da seguinte forma:

1 main = do2 as <- mapM (async . getURL) sites3 rs <- mapM wait as4 mapM_ (r -> print $ B.length r) rs

55

Page 61: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Tratamento de Erro

E se uma das URLs não existir ou retornar algum erro?Devemos jogar fora todos os resultados e exibir umamensagem de erro?

A biblioteca async define a função:

1 waitCatch :: Async a -> IO (Either SomeException a)

Que retorna ou um erro, que pode ser tratado, ou o valoresperado. Para tratar o erro devemos importar também importControl.Exception

56

Page 62: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Either

O tipo Either é definido como:

1 data Either a b = Left a | Right b

e assim como o Maybe é utilizado para tratamento de erro. Elediz: ”ou vou retornar algo do tipo a ou do tipo b”, sendo o tipoa geralmente tratado como o erro.

57

Page 63: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Tratamento de Erro

Então podemos definir:

1 printLen :: (Either SomeException B.ByteString) -> IO ()2 printLen (Left e) = print "URL not found"3 printLen (Right b) = print $ B.length b

58

Page 64: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Tratamento de Erro

E então:

1 main = do2 as <- mapM (async . getURL) sites3 rs <- mapM waitCatch as4 mapM_ printLen rs

59

Page 65: Haskell Concorrente - MCTA016-13 - Paradigmas de Programaçãoprofessor.ufabc.edu.br/~e.francesquini/2019.q2... · HaskellConcorrente MCTA016-13-ParadigmasdeProgramação EmilioFrancesquini

Leituras complementares

Com isso finalizamos o assunto de Programação Concorrentenessa disciplina, embora não tenhamos esgotado todos osconceitos.

Para quem quiser avançar no assunto, a leitura do livro doSimon Marlow é obrigatória!

60