O documento discute programação concorrente, abordando modelos de comunicação entre tarefas como memória compartilhada e troca de mensagens, além de técnicas para coordenar acesso a recursos compartilhados. Também apresenta linguagens para programação concorrente como Erlang, Limbo e OCCAM, destacando recursos e exemplos de código em cada uma.
1. Disciplina: Sistemas Operacionais
Professor: Arildo Antônio Sônego
Acadêmicos: Fábio Duarte de Souza
Mateus Luiz
Título: Linguagens de Programação Concorrente
Programação Concorrente
Programação concorrente é uma visão que o programador possui sobre a estruturação
e execução de um programa. Por exemplo, em programação orientada a objetos, programadores
podem abstrair um programa como uma coleção de objetos que interagem entre si, enquanto em
programação funcional os programadores abstraem o programa como uma sequência de funções
executadas de modo empilhado.
Para a construção de programas e execução concorrente (simultânea) de várias
tarefas computacionais interativas, que podem ser implementadas como programas separados ou
como um conjunto de thread criadas por um único programa.
Essas tarefas também podem ser executadas por um único processador, vários
processadores em um único equipamento ou processadores distribuídos por uma rede.
Programação concorrente é relacionada com programação paralela, mas foca mais na
interação entre as tarefas. A interação e a comunicação correta entre as diferentes tarefas, além da
coordenação do acesso concorrente aos recurso computacionais são as principais questões
discutidas durante o desenvolvimento de sistemas concorrentes. Pioneiros na área de
programação concorrente incluem Edsger Dijkstra, Per Brinch Hansen, e C. A. R. Hoare.
Interação e comunicação concorrente
Em alguns sistemas computacionais concorrentes, a comunicação entre os
componentes é escondida do programador, enquanto em outros a comunicação deve ser lidada
explicitamente. A comunicação explícita pode ser dividida em duas classes:
Comunicação por memória compartilhada
Componentes concorrentes comunicam-se ao alterar o conteúdo de áreas de memória
compartilhadas. Java e C# são linguagens que utilizam tal modelo. Esse estilo de programação
geralmente requer o desenvolvimento de alguns métodos de trava como mutex, semáforo ou
monitor para gerenciar a utilização da memória entre as tarefas.
Comunicação por troca de mensagens
Componentes concorrentes comunicam-se ao trocar mensagens. Erlang e Occam são
linguagens que utilizam tal modelo. A troca de mensagens pode ser lidada assincronamente
(também denominada como "enviar e rezar", apesar da prática padrão ser reenviar mensagens que
não são sinalizadas como recebidas) ou pelo método rendezvous, no qual o emissor é bloqueado
até que a mensagem seja recebida.
A comunicação por mensagens tende a ser mais simples que a comunicação por
memória compartilhada, e é considerada como uma forma mais robusta de programação
concorrente.
Coordenando o acesso aos recursos
2. Um dos assuntos de maior discussão em programação concorrente é como prevenir
que tarefas concorrentes interfiram umas nas outras. Por exemplo, considerando o seguinte
algoritmo para realizar saques de uma conta representada pelo recurso compartilhado balanco:
1 bool saque(int quantia) {
2 if( balanco > quantia ) {
3 balanco = balanco - quantia;
4 return true;
5 } else return false;
6 }
Suponha que balanco = 500, e dois processos concorrentes realizam a chamada
saque(300) e saque(350) . Se em ambas as operações a linha 2 é executada antes da linha 3 do
processo concorrente, ambas as operações irão deduzir que o balanço é maior que a quantia a ser
sacada, e a execução irá proceder subtraido os valores a serem sacados em ambos os processos.
Apesar disso, como ambos os processos realizam o saque, o balanço acaba ficando com valor
negativo, um resultado que não deveria acontecer. Esses tipos de probemas com recursos
compartilhados requerem o uso de controles concorrentes, ou algoritmos não bloqueantes.
Como sistemas concorrentes necessitam a utilização de recursos compartilhados, a
programação concorrente geralmente requer o uso de algum método de árbitro, um elemento
neutro, para coordenar o acesso a tais recursos. Isso introduz a possibilidade do aparecimento de
problemas com decisões não determinísticas, apesar de que o desenvolvimento cuidadoso de
árbitros pode reduzir a probabilidade de tais situações aparecerem.
Linguagens para programação concorrente
As linguagens de programação concorrente são linguagens de programação que usam
construções para a concorrência. Tais construções podem envolver multi-tarefa, suporte para
sistemas distribuídos, troca de mensagens e recursos compartilhados.
Atualmente, as linguagens mais utilizadas para tais construções são Java e C#. Ambas
utilizam o modelo de memória compartilhada, com o bloqueio sendo fornecido por monitores.
Apesar disso, o modelo de troca de mensagens pode ser implementado sobre o modelo de
memória compartilhada. Entre linguagens que utilizam o modelo de troca de mensagens, Erlang é
possivelmente a mais utilizada pela indústria atualmente.
Várias linguagens de programação concorrente foram desenvolvidas como objeto de
pesquisa, como por exemplo Pict. Apesar disso, linguagens como Erlang, Limbo e Occam tiveram
uso industrial em vários momentos desde a década de 1980.
Várias outras linguagens oferecem o suporte à concorrência através de bibliotecas,
como por exemplo C e C++.
Linguagem de Programação Limbo
É uma linguagem de programação que foi desenvolvida pela Lucent Technologies Inc.
para a execução de aplicações distribuídas de pouca escalabilidade (aplicações que executam
sobre um número pequeno de nodos).
Limbo é uma linguagem de programação imperativa , que apesar de rodar em cima de
uma máquina virtual, não é considerada orientada a objetos, pois não possui suporte a herança e
nem a definição de classes .
Byte unsigned (8-bits)
3. int signed (32-bits)
big signed (64-bits)
real long float (64-bits)
list,array
String
channel (para comunicaçao entre processos)
adt (análogo ao struct presente em C)
pick (análogo ao union presente em C)
module
Exemplo de código da Linguagem Limbo:
hello.b)
implement Hello;
include "sys.m"; //biblioteca da linguagem Limbo
sys: Sys;
include "draw.m";
Hello: module
{
init: fn(ctxt: ref Draw->Context, argv: list of string);
};
init(ctxt: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
sys->print("hello, worldn");
}
OCCAM
OCCAM é uma linguagem para aplicações científicas e de engenharia, para processos
de controle industrial e para sistemas embarcados. OCCAM foi originalmente projetado para
transputer (contração entre transistor e computer), uma arquitetura microprocessada, desenvolvida
pelo INMOS, que suporta concorrência e sincronização.
A OCCAM possui características especiais para solução de problemas de
programação concorrente (cf. CARNEIRO, 2001, p.35) e a semântica formal da linguagem auxilia a
transformação e validação dos programas em modelos matemáticos.
Em OCCAM, as partes de um programa são tomadas como processos que executam
suas funções e terminam. Pode-se ter mais de um processo sendo executado concorrentemente e
processos podem trocar mensagens entre si (cf. CARNEIRO, 2001, p.35).
A comunicação é feita através de um construtor especial da linguagem chamado canal.
Um canal é um caminho unidirecional de comunicação entre dois processos concorrentes e é
implementado na prática usando regiões alocadas de memória, se os processos estão residentes
no mesmo transputer, ou por links de comunicação dos transputers, se os processos comunicantes
estiverem em diferentes transputers.
P1 P2
SEQ SEQ
canal1 ! A canal2 ! X
canal2 ? B canal1 ? X
4. PAR
SEQ
canal3 ? valor1
valor := valor1 + 1
SEQ
canal4 ! valor2
valor2 := valor2 + 1
Linguagem de programação Erlang
Erlang é uma linguagem de programação declarativa, para programação paralela e
distribuída. Seu “jeitão” é muito mais parecido com Prolog ou Lisp que com C ou Java… e não é
orientada a objetos, é “orientado à concorrência”, como costuma-se dizer.
Tanto a AMD quanto a Intel estão criando processadores dual core e quad core para
as massas. A Intel também tem o Hyper-Threading, que trata um núcleo como se fossem dois. Um
processador Intel Xeon quad core com HT comporta-se como se fossem 8 processadores!
Atualmente temos o Pentium Itanium, Pentium Xeon, Pentium 4 HT e AMD Opteron que podem ser
utilizados de forma paralela (ambos os núcleos trabalhando ao mesmo tempo).
O bacana do Erlang é que ela tira o máximo proveito de paralelismo com
processadores. Na teoria, um programa em Erlang é N vezes mais rápido que um programa em
outras linguagens sem paralelismo, sendo N o número de processadores na máquina. Na teoria
pois nem sempre o processador é o gargalo da performance, pode ser leitura/escrita em disco,
acesso à memória, interface com outros dispositivos, sistema operacional, etc.
Código escrito em Erlang
-module(hello_concurrent).
-export([receiver/0. giver/1. start/0]).
receiver() ->
receive
diediedie -> ok;
{ name, Name} -> io:format(“hello, ~s~n”, [Name]), receiver()
end.
giver(ReceiverPid) ->
ReceiverPid = spawn(hello_concurrent, receiver, [] ),
spawn(hello_concurrent, giver, [ReceiverPid]),
start_finished.