Programação Concorrente e ToonTalk

Antes de começar a explicar a programação concorrente em ToonTalk e as razões pelas quais é complicado criar extensões concorrentes em linguagens sequenciais, vou procurar explicar a diferença entre programação concorrente, paralela e distribuída. Os processos paralelos são processos que estão a ser executados simultaneamente no mesmo computador. Basicamente o computador tem vários processadores que se encarregam dos vários processos em paralelo. O processamento distribuído controla processos que estão a ser executados em vários computadores ligados através de uma rede. Esta distinção é importante, porque a computação distribuída implica protocolos de confiança entre os sistemas  implicados e coloca questões de segurança, atribuição de recursos, fiabilidade das ligações, etc... Em situações nas quais esta distinção não é crucial, podemos falar simplesmente de programação concorrente.

O ToonTalk é uma linguagem de programação concorrente. Todas as rotinas e sub-rotinas de programação são expressas como processos independentes. No caso de um robot precisar de outros robots para efectuar sub-computações, tem que ser treinado para carregar um camião com os outros robots. A nova casa construída pela tripulação é um novo processo. Ao contrário das linguagens convencionais (C, Java, Logo, Lisp, Fortran, Cobol, Perl, Basic, etc.), em ToonTalk, não há forma de expressar o conceito de subrotina. Nessas linguagens, uma chamada a um procedimento faz com que o programa aguarde até que o processo termine e, eventualmente, devolva um valor, após o que o programa pode continuar a computação. O comportamento equivalente em ToonTalk obtém-se treinando um robot para carregar um camião com robots e uma caixa com um pássaro. O robot deve colocar o ninho do pássaro na sua caixa e nada mais. Este robot (ou outros elementos da equipa de robots) deve ser treinado para procurar qualquer coisa no ninho. Consequentemente, este processo será suspenso até que os robots que foram colocados no camião dêem alguma coisa ao pássaro. O robot ficará no ninho à espera até que algo seja entregue após o que a computação prosseguirá. Por outras palavras, uma chamada a uma subrotina ou procedimento em ToonTalk é apenas um caso especial da utilização de camiões, pássaros  e robots.  ToonTalk apenas dá ao programador uma forma geral de expressar a criação de novos processos.

O facto de não haver subrotinas no ToonTalk permite a existência de muito maior número de processos que noutras linguagens tradicionais. O motivo é que normalmente as subrotinas são implementadas com base numa estrutura de dados chamada pilha. As pilhas são muito eficientes na implementação de chamadas a procedimentos, incluindo chamadas recursivas. O problema é que cada processo (mesmo que suspenso) precisa da sua própria pilha. Isto faz com os processos consumam rapidamente todos os recursos disponíveis. Pelo contrário, um processo em ToonTalk precisa apenas de dois ponteiros: um para o programa (a equipa de robots), outro para os dados (a caixa). Não se utilizam pilhas. O ToonTalk foi testado com dezenas de milhares de casas (processos), Pelo contrário em experiências anteriores com Java apenas se conseguiram umas escassas centenas de processos.

As linguagens convencionais assentam em estados partilhados. A mesma variável, estruturas de dados, e objectos podem ser acedidos a partir de diferentes processos (processos que partilham os mesmos dados, são, por normalmente, threads). A partilha de estado é necessária, nestas linguagens, de forma a que as threads possam trabalhar em conjunto. Esta partilha entre processos concorrentes independentes é, no entanto, demasiado perigosa, pois conduz a situações de competição. Consideremos a variável A, que representa o balanço actual de uma conta poupança. (A pode ser uma variável global ou uma instância de um objecto - ambos os tipos de variáveis têm este problema.). Existe uma rotina para retirar X de A depois de verificar se X é menor ou igual a A. O levantamento é feito atribuindo a A A-X. Por outras palavras, só acontece se existir dinheiro suficiente. Introduzamos agora processos concorrentes. Imaginemos um saldo de 10.000$ e dois processos para levantar respectivamente 9.000$ e 8.000$. Com algum azar os dois levantamentos serão efectuados. Pior que isso o saldo final tanto pode ficar 1.000$, como 2.000$, como -7.000$. Como assim? Imaginemos o seguinte cenário:

O processo 1 verifica se A é menor ou igual a X e efectua A-X=1.000$, mas antes de atribuir o valor 1.000$ a A, o processo 2 ocorre e verifica que A=10.000$ e atribui a A o valor 2.000$. O processo 1 continua e atribui a A o valor 1.000$. 

Então como é que as linguagens convencionais tratam os problemas de competição? Introduzem um novo construto:  bloqueio de dados críticos. Isto porém cria novos problemas pois o processo 1 pode precisar de dados que foram bloqueados pelo processo 2 e vice-versa o que pode conduzir a situações de bloqueio total.

Como é que o ToonTalk ultrapassa problemas deste tipo? Simplesmente não usando estados partilhados. Uma caixa só pode existir num lugar. Por sua vez, numa mesma casa os robots trabalham em turnos e nunca acedem à mesma caixa ao mesmo tempo. Como não existem estados partilhados também não existem problemas de competição, nem de bloqueio total. Mas, será que isto não limita o ToonTalk? De facto seria uma limitação se não existissem os pássaros. Dado que múltiplas cópias do mesmo pássaro podem voar para o mesmo ninho, isto possibilita implementar comunicações do tipo muitos para um em vez dos estados partilhados. Consideremos novamente o exemplo da conta bancária. Robots em diferentes casas podem dar a pássaros desta conta ordens de levantamento, dando-lhes uma caixa especificando a quantidade a levantar. Não sabemos, à partida, a ordem pela qual as caixas irão chegar ao ninho - no entanto, todas elas serão empilhadas pela ordem de chegada. Os robots que trabalham com a caixa da conta verão chegar uma caixa ao ninho para efectuar um levantamento. Tratarão cada pedido sequencialmente. Isto não traz qualquer problema ainda que o cálculo do novo saldo exija um sub-cálculo. Neste caso o robot carregará um camião com uma equipa de robots e uma caixa para calcular o novo saldo, após o que colocará o ninho que irá receber a resposta no local onde onde está guardado o saldo. Os pedidos enviados ao ninho só serão processados à medida que o sub-cálculo entrega ao pássaro o novo saldo e o ninho respectivo é coberto.

Em conclusão, as tentativas para adicionar processos concorrentes a linguagens com sub-rotinas e estados partilhados envolve aumento de complexidade, custo adicional na implementação de processos e a ocorrência de erros muito difíceis de detectar. O ToonTalk, pelo contrário, foi desenhado de raiz para ser concorrente. Em ToonTalk, não há, pois, necessidade de introduzir bloqueios a dados críticos, os processos consomem menos recursos e não existem problemas de competição e processo bloqueados. 

principal | procura | compra | manual | notícias | informação | perguntas | apoio | downloads | imprensa | contacto