Archive for the ‘programação’ Category

Os três principais problemas da Concorrência

Wednesday, September 21st, 2011

Ultimamente se tornou inevitável desenvolver programas que funcionam de maneira concorrente, já que os processadores com multiplos cores já estão em todo lugar, até mesmo nos celulares. E ao mesmo tempo que é extremamente prazeroso ver o seu software muito mais rápido graças a concorrência, também se pode perder muitas noites de sono com os problemas causados por erros no desenvolvimento de software concorrente. Neste post eu pretendo falar de maneira breve dos três principais problemas que podem surgir quando se desenvolve software concorrente. São eles: Starvation, Deadlocks e Race Conditions.

Starvation

Starvation ocorre quando uma thread fica esperando uma resposta pra prosseguir e essa resposta nunca chega, logo, a thread permanece viva, ocupando recursos, mas não produzindo nada de útil para o software. Uma boa forma de tratar esse problema é sempre trabalhar com timeout e fluxo de exceção, para que caso a resposta não chegue em um determinado tempo seja seguido o fluxo de exceção e a thread possa finalizar o seu trabalho.

Deadlocks

Deadlock ocorre se duas ou mais threads ficam aguardando uma a outra para realizar alguma ação ou liberar algum recurso, por exemplo, a thread A ficar aguardando a thread B liberar algum recurso e B ficar aguardando A terminar alguma ação pra liberar esse recurso, logo, ambas vão permanecer eternamente esperando. Não existe uma solução simples pra resolver esse problema como o timeout para o problema de Starvation, mas se a aplicação não possuir locks explicitos e estados mutáveis então ela estará a salvo de deadlocks, então esse pode ser um bom caminho a se seguir.

Race Conditions

Race Condition ocorre se duas threads concorrem para usar o mesmo recurso ou dado. É um problema bem difícil de tratar pois o comportamento da aplicação se torna imprevisível e uma Race Condition pode ocorrer depois de meses do software em produção. Pra piorar, este problema não ocorre apenas quando duas threads tentam modificar o mesmo dado, ele pode correr quando uma thread tenta gravar um dado e a outra está tentando apenas ler este mesmo dado. Pra piorar ainda mais, existem casos aonde se o código não é otimizado com O JIT(Just In Time) Compiler a Race Condition não aparece, mas se o código é elegível ao JIT e é compilado então a Race Condition surge, basicamente porque a JVM passa a usar cache para acessar algumas variáveis e se outra thread muda o valor da variável, esse valor não é replicado para o cache. Neste caso citado a solução seria usar volatile e/ou synchronized.

Novamente não usar objetos mutáveis evitaria o problema de Race Conditions, por isso que imutabilidade tem sido um tema tão comentado ultimamente, e linguagens que pregam essa técnica tem ganhado cada vez mais força.

Para se aprofundar mais no assunto, inclusive com um código de exemplo que mostra uma Race Condition causada pelo JIT, recomendo a leitura do livro “Programming Concurrency on the JVM”.

A linguagem certa para cada problema! (Problema do Sudoku)

Sunday, September 12th, 2010

Cada vez mais na área de programação aquela história de se especializar em uma linguagem, e resolver todos os problemas com ela está sendo questionada. E realmente, como vocês verão neste post, esse questionamento faz todo sentido. Existe uma linguagem mais adequada para resolver cada problema, e por isso, é obrigação do programador conhecer diversas linguagens, no mínimo uma de cada paradigma, para poder escolher qual linguagem se encaixa melhor para determinado problema.

Considere o problema de implementar um resolvedor de Sudoku, ou seja, você deve implementar um programa que dado um tabuleiro do jogo parcialmente preenchido, seu programa deve devolver o tabuleiro totalmente preenchido.

Não sei se é a melhor que existe, mas a melhor linguagem que eu conheço pra resolver este problema é Prolog. Sim, aquela linguagem de programação que a gente vê na faculdade no curso de Inteligência Artificial. Prolog é uma linguagem de programação declarativa, baseada em lógica de primeira ordem. Em Prolog temos:

    -Fatos: Uma afirmação básica. (Ex: Azalão é um cavalo; cavalo gosta de grama.)

    -Regras: É uma inferência sobre um fato. (Ex: Um animal gosta de grama se ela é um cavalo.)

    -Query: Uma questão (Ex: Azalão gosta de grama?)

E é isso, ou seja, bem diferente de uma linguagem de programação imperativa, como Java por exemplo.

Agora vamos definir as regras para verificar se um jogo de Sudoku é valido, e embaixo de cada regra vamos definir o código em Prolog para implementar tal regra. São elas:

Supondo que nosso problema é definido como:

sudoku(Entrada, Solucao)

Regra 1) Para um jogo resolvido, os números na entrada e na saída devem ser os mesmos.

sudoku(Entrada, Solucao) :- Solucao = Entrada.

Regra 2) Um tabuleiro de Sudoku é uma grade de 81 elementos, com valores de 1-9.

Entrada = [S11, S12, S13, S14, S15, S16, S17, S18, S19,
		  S21, S22, S23, S24, S25, S26, S27, S28, S29,
		  S31, S32, S33, S34, S35, S36, S37, S38, S39,
		  S41, S42, S43, S44, S45, S46, S47, S48, S49,
		  S51, S52, S53, S54, S55, S56, S57, S58, S59,
		  S61, S62, S63, S64, S65, S66, S67, S68, S69,
		  S71, S72, S73, S74, S75, S76, S77, S78, S79,
		  S81, S82, S83, S84, S85, S86, S87, S88, S89,
		  S91, S92, S93, S94, S95, S96, S97, S98, S99],
	fd_domain(Solucao, 1, 9).

Regra 3) Um tabuleiro tem 9 linhas, 9 colunas e 9 quadrados.

Row1 = [S11, S12, S13, S14, S15, S16, S17, S18, S19],
Row2 = [S21, S22, S23, S24, S25, S26, S27, S28, S29],
Row3 = [S31, S32, S33, S34, S35, S36, S37, S38, S39],
Row4 = [S41, S42, S43, S44, S45, S46, S47, S48, S49],
Row5 = [S51, S52, S53, S54, S55, S56, S57, S58, S59],
Row6 = [S61, S62, S63, S64, S65, S66, S67, S68, S69],
Row7 = [S71, S72, S73, S74, S75, S76, S77, S78, S79],
Row8 = [S81, S82, S83, S84, S85, S86, S87, S88, S89],
Row9 = [S91, S92, S93, S94, S95, S96, S97, S98, S99],

Col1 = [S11, S21, S31, S41, S51, S61, S71, S81, S91],
Col2 = [S12, S22, S32, S42, S52, S62, S72, S82, S92],
Col3 = [S13, S23, S33, S43, S53, S63, S73, S83, S93],
Col4 = [S14, S24, S34, S44, S54, S64, S74, S84, S94],
Col5 = [S15, S25, S35, S45, S55, S65, S75, S85, S95],
Col6 = [S16, S26, S36, S46, S56, S66, S76, S86, S96],
Col7 = [S17, S27, S37, S47, S57, S67, S77, S87, S97],
Col8 = [S18, S28, S38, S48, S58, S68, S78, S88, S98],
Col9 = [S19, S29, S39, S49, S59, S69, S79, S89, S99],

Square1 = [S11, S12, S13, S21, S22, S23, S31, S32, S33],
Square2 = [S14, S15, S16, S24, S25, S26, S34, S35, S36],
Square3 = [S17, S18, S19, S27, S28, S29, S37, S38, S39],
Square4 = [S41, S42, S43, S51, S52, S53, S61, S62, S63],
Square5 = [S44, S45, S46, S54, S55, S56, S64, S65, S66],
Square6 = [S47, S48, S49, S57, S58, S59, S67, S68, S69],
Square7 = [S71, S72, S73, S81, S82, S83, S91, S92, S93],
Square8 = [S74, S75, S76, S84, S85, S86, S94, S95, S96],
Square9 = [S77, S78, S79, S87, S88, S89, S97, S98, S99].

Regra 4) Não pode haver números repetidos nas linhas, colunas e quadrados.

valid([]).
valid([Head | Tail]) :-
	fd_all_different(Head), valid(Tail).

valid([Row1, Row2, Row3, Row4, Row5, Row6, Row7, Row8, Row9,
     Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9,
     Square1, Square2, Square3, Square4, Square5, Square6, Square7, Square8, Square9]).

E pronto, acredite ou não, o problema está resolvido. Como vocês viram, a única coisa que fizemos foi definir as regras do problema e pronto, o Prolog resolve o problema pra você. Agora pense como você resolveria esse problema em Java por exemplo. Com certeza definir as regras acima seria apenas o começo.

Mas muitos devem estar se perguntando: “Tá, mas meu programa é feito em Java, o que eu vou fazer com esse código em Prolog?”. Mas é aí que entra o principal ponto, é possível integrar código em Prolog com diversas outras linguagems, por isso que hoje em dia não é mais preciso resolver todos os problemas de um sistema com a mesma linguagem.

E aí, animou a conhecer mais Prolog?

Segue abaixo o código completo do problema. Para rodar essa implementação é necessário o gprolog, já que usamos algumas funções pré-definidas existentes apenas nesta ferramenta.

valid([]).
valid([Head | Tail]) :-
	fd_all_different(Head), valid(Tail).

sodoku(Puzzle, Solution) :-
	Solution = Puzzle,
	Puzzle = [S11, S12, S13, S14, S15, S16, S17, S18, S19,
		  S21, S22, S23, S24, S25, S26, S27, S28, S29,
		  S31, S32, S33, S34, S35, S36, S37, S38, S39,
		  S41, S42, S43, S44, S45, S46, S47, S48, S49,
		  S51, S52, S53, S54, S55, S56, S57, S58, S59,
		  S61, S62, S63, S64, S65, S66, S67, S68, S69,
		  S71, S72, S73, S74, S75, S76, S77, S78, S79,
		  S81, S82, S83, S84, S85, S86, S87, S88, S89,
		  S91, S92, S93, S94, S95, S96, S97, S98, S99],
	fd_domain(Solution, 1, 9),

	Row1 = [S11, S12, S13, S14, S15, S16, S17, S18, S19],
	Row2 = [S21, S22, S23, S24, S25, S26, S27, S28, S29],
	Row3 = [S31, S32, S33, S34, S35, S36, S37, S38, S39],
	Row4 = [S41, S42, S43, S44, S45, S46, S47, S48, S49],
	Row5 = [S51, S52, S53, S54, S55, S56, S57, S58, S59],
	Row6 = [S61, S62, S63, S64, S65, S66, S67, S68, S69],
	Row7 = [S71, S72, S73, S74, S75, S76, S77, S78, S79],
	Row8 = [S81, S82, S83, S84, S85, S86, S87, S88, S89],
	Row9 = [S91, S92, S93, S94, S95, S96, S97, S98, S99],

	Col1 = [S11, S21, S31, S41, S51, S61, S71, S81, S91],
	Col2 = [S12, S22, S32, S42, S52, S62, S72, S82, S92],
	Col3 = [S13, S23, S33, S43, S53, S63, S73, S83, S93],
	Col4 = [S14, S24, S34, S44, S54, S64, S74, S84, S94],
	Col5 = [S15, S25, S35, S45, S55, S65, S75, S85, S95],
	Col6 = [S16, S26, S36, S46, S56, S66, S76, S86, S96],
	Col7 = [S17, S27, S37, S47, S57, S67, S77, S87, S97],
	Col8 = [S18, S28, S38, S48, S58, S68, S78, S88, S98],
	Col9 = [S19, S29, S39, S49, S59, S69, S79, S89, S99],

	Square1 = [S11, S12, S13, S21, S22, S23, S31, S32, S33],
	Square2 = [S14, S15, S16, S24, S25, S26, S34, S35, S36],
	Square3 = [S17, S18, S19, S27, S28, S29, S37, S38, S39],
	Square4 = [S41, S42, S43, S51, S52, S53, S61, S62, S63],
	Square5 = [S44, S45, S46, S54, S55, S56, S64, S65, S66],
	Square6 = [S47, S48, S49, S57, S58, S59, S67, S68, S69],
	Square7 = [S71, S72, S73, S81, S82, S83, S91, S92, S93],
	Square8 = [S74, S75, S76, S84, S85, S86, S94, S95, S96],
	Square9 = [S77, S78, S79, S87, S88, S89, S97, S98, S99],

	valid([Row1, Row2, Row3, Row4, Row5, Row6, Row7, Row8, Row9,
	       Col1, Col2, Col3, Col4, Col5, Col6, Col7, Col8, Col9,
	       Square1, Square2, Square3, Square4, Square5, Square6, Square7, Square8, Square9]).

Segue um exemplo de entrada:

sodoku([9,4,_,1,_,2,_,5,8,
 6,_,_,_,5,_,_,_,4,
 _,_,2,4,_,3,1,_,_,
 _,2,_,_,_,_,_,6,_,
 5,_,8,_,2,_,4,_,1,
 _,6,_,_,_,_,_,8,_,
 _,_,1,6,_,8,7,_,_,
 7,_,_,_,4,_,_,_,3,
 4,3,_,5,_,9,_,1,2], Solution).

Abraços e até a próxima!

O que esperar das linguagens de programação para o futuro?

Saturday, July 24th, 2010

Dizem que os melhores perfumes são aqueles que estão nos menores frascos. Ao menos pelo show que o Rod Pike do Google deu quando falou no OSCON 2010, parece que isso vale para palestras também. Foram doze minutos e meio de um conteúdo riquissímo pra fazer qualquer programador refletir.

Nesta palestra, o Rod tentou mostrar como as principais linguagens que dominam o mercado hoje são burocráticas e chatas de trabalhar. Ele deixa claro que são linguagens robustas e de qualidade, isso é um fato inquestionável, mas elas podiam ser mais amigáveis.

O Rod usou a conhecida do frase do Norvig que diz “patterns are a a demonstration of weakness in a language” para embassar seus argumentos, e também não deixou de mostrar muitos códigos para comprovar o quão verboso é fazer algumas coisas em linguagens como o Java por exemplo. Além disso, também foi falado que o hardware evoluiu muito esses últimos anos, hoje em dia qualquer um tem um computador com dois cores no mínimo, mas nas linguagens de programação tradicionais por assim dizer, não é nada trivial fazer uso deste poder.

Após mostrar todos esses pontos, o Rod expõe quais são os objetivos das linguagens de programação para o futuro. Ou seja, o que precisa ser feito para que elas se tornem melhores, e no final, puxa um pouquinho a sardinha pro lado dele, falando um pouco da Go, a linguagem de programação do Google.

Um fato importante é que para conseguir ver tudo isso que o Rod viu e falou, é preciso pensar fora da caixa como dizem por aí, e aprender novas linguagens de programação. Eu atualmente tenho dado uma olhada mais profunda em duas: Python e Haskell, e não estou me arrependendo. O pessoal pode falar: “Mas aonde você vai usar Haskell profissionalmente?”. Bom, talvez eu nunca use Haskell profissionalmente, mas com certeza seja qual for a linguagem que eu trabalhar, aprender Haskell e programação funcional me tornará um programador melhor.

Enfim, para quem ainda não viu, não deixe de ver essa palestra. E viva a internet, aonde você consegue ter acesso a uma palestra de um evento de fora do país quase que no mesmo dia em que a palestra foi dada.