Código

Geleia de Código

8

jam2014

Não costumo participar de campeonatos de programação por alguns motivos vagos: é perda de tempo (não ganho nada com isso), sou um péssimo programador (ou pasteleiro), dá preguiça (esse é o mais válido) e por aí vai o mimimi. Dessa forma, sempre passei ileso de eventos como o atual Google Code Jam, que pretende levar a categoria de código ofuscado para um novo patamar.

No entanto, esse ano apareceram dois motivos que me levaram a gastar cinco minutos de paciência com as historinhas bestas da equipe do Google. Primeiro o Python, que desde 2013 tem renovado em mim a sensação que programar ainda é divertido (e que o pessoal da Microsoft e do padrão C++ tinham tirado de mim há muito tempo com seus compiladores cada vez mais complexos/lentos e as IDEs que demoram o tempo do cafezinho para abrir). Segundo o que move o mundo: a concorrência. Minha digníssima esposa, levada por alguns pontos-extra na faculdade (uma iniciativa até que louvável do professor), resolveu participar da primeira fase (a classificação desta fase também dava pontos).

O fato é que depois desses cinco minutos eu simplesmente não consegui parar até o minuto final das 23 horas (horário de Brasília) de domingo, quando o tempo-limite esgotou. O aspecto mais divertido do Code Jam é que há liberdade total para a ferramenta que você pretende usar: linguagens de programação, Excel, uma calculadora ou apenas seu cérebro. Você recebe uma "missão" e um arquivo de entrada e precisa cuspir um arquivo de saída de acordo com a missão. Apenas isso. O resto fica por conta da criatividade dos codadores e gambiarreiros de plantão.

Todos os exercícios levam em consideração um arquivo de entrada que possui em sua primeira linha o número de testes que serão feitos e em seguida um número determinado de linhas e parâmetros, geralmente divididos por espaço. O primeiro problema, por exemplo, apenas considerava a suposição de cartas em pequeno truque de mágica e recebia como entrada a disposição dessas cartas junto com a escolha da fileira que o participante dizia onde estava a carta escolhida.

2
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
3
1 2 5 4
3 11 6 15
9 10 7 12
13 14 8 16
import sys
 
f = open(sys.argv[1])
total = int(f.readline())
 
for case in range(0, total):
	guess1 = int(f.readline())
	row1 = None
	for i in range(1, 5):
		row = f.readline().split()
		if i == guess1:
			row1 = row
	guess2 = int(f.readline())
	row2 = None
	for i in range(1, 5):
		row = f.readline().split()
		if i == guess2:
			row2 = row
	cards = list(set(row1) & set(row2))
	if len(cards) == 1:
		print 'Case #' + str(case+1) + ': ' + cards[0]
	elif len(cards) > 1:
		print 'Case #' + str(case+1) + ': Bad magician!'
	else:
		print 'Case #' + str(case+1) + ': Volunteer cheated!'

O segundo exercício já envolvia um jogo bem divertido em que o jogador ficava clicando em cookies como se não houvese amanhã. Esse deu um pouco mais de trabalho, mas foi mais divertido que o primeiro.

import sys
 
def CookieClicker(farmCost, farmIncrement, cookieTarget):
	cookiePerSecond = 2.0
	bestTime = cookieTarget / cookiePerSecond # melhor tempo soh fazendo cookies
	cookieFarmTime = cookieTarget / (cookiePerSecond + farmIncrement) # melhor tempo ja com fazenda criada
	farmTime = farmCost / cookiePerSecond + cookieFarmTime # quanto vai custar fazer a fazenda e depois fazer cookies com a fazenda
	while farmTime < bestTime: # enquanto fazer a fazenda custar menos tempo que soh fazer cookies...
		bestTime = farmTime # por enquanto melhor tempo
		cookiePerSecond = cookiePerSecond + farmIncrement # novo tempo para fazer cookies (mais uma fazenda ja criada)
		farmTime = farmTime - cookieFarmTime # tiramos o tempo de soh fazer cookies para fazer mais uma fazenda
		cookieFarmTime = cookieTarget / (cookiePerSecond + farmIncrement) # novo tempo com mais uma fazenda criada
		farmTime = farmTime + farmCost / cookiePerSecond + cookieFarmTime # agora com o novo tempo de fazer outra fazenda e soh cookies
	return bestTime
 
f = open(sys.argv[1])
total = int(f.readline())
 
for case in range(1, total + 1):
    args = [float(i) for i in f.readline().split()]
    ret = CookieClicker(args[0], args[1], args[2])
    print 'Case #' + str(case) + ': ' + '{0:.7f}'.format(ret)

Já o terceiro... o terceiro passa. Vamos para o quarto, um dos mais instigantes, pois envolve duas regras distintas de um jogo e a otimização das melhores estratégias para ambos. Isso consumiu bem mais tempo que os outros dois iniciais, pois lembro de ter me isolado por uma hora para conseguir colocar tudo na cabeça.

import sys
 
def BestBlock(block, blocks):
	bestBlock = 0
	for i in range(len(blocks) - 1, -1, -1):
		if blocks[i] < block: break
		bestBlock = blocks[i]
	if not bestBlock:
		bestBlock = blocks[0]
	return bestBlock
 
def War(naomi, ken):
	naomi = sorted(naomi)
	ken = sorted(ken)
	naomiCount = 0
	while len(naomi):
		naomiBlock = naomi[-1]
		kenBlock = BestBlock(naomiBlock, ken)
		if naomiBlock > kenBlock:
			naomiCount = naomiCount + 1
		#print str(naomiBlock) + ' vs ' + str(kenBlock) + ': ' + str(naomiCount)
		naomi.remove(naomiBlock)
		ken.remove(kenBlock)
	return naomiCount
 
def WarCheat(naomi, ken):
	naomi = sorted(naomi)
	ken = sorted(ken)
	naomiCount = 0
	while len(naomi):
		naomiTold = 0
		naomiBlock = naomi[-1]
		bestKen = ken[-1]
		if naomiBlock > bestKen:
			naomiTold = bestKen + 0.00000001
		else:
			naomiTold = bestKen - 0.00000001
		kenBlock = BestBlock(naomiTold, ken)
		naomiBlock = BestBlock(kenBlock, naomi)
		if naomiBlock > kenBlock:
			naomiCount = naomiCount + 1
		#print str(naomiTold) + '(' + str(naomiBlock) + ') vs ' + str(kenBlock) + ': ' + str(naomiCount)
		naomi.remove(naomiBlock)
		ken.remove(kenBlock)
	return naomiCount
 
f = open(sys.argv[1])
total = int(f.readline())
 
for case in range(1, total + 1):
        f.readline()
        naomi = [float(i) for i in f.readline().split()]
        ken = [float(i) for i in f.readline().split()]
        war = War(naomi, ken)
        warCheat = WarCheat(naomi, ken)
        print 'Case #' + str(case) + ': ' + str(warCheat)+ ' ' + str(war)

Já o terceiro foi um fracasso total. Tentei de todas as maneiras resolver o impasse de descobrir qual disposição de um jogo de campo minado poderia ser resolvido em apenas um clique (parece o jogo oposto do viciado clicador de cookies), mas falhei miseravelmente. E desconfio o porquê. Primeiro entendo que meu perfeccionismo me impediu de realizar uma checagem padrão para exceções já conhecidas (quando há apenas uma linha ou coluna, quando há apenas um espaço sem minas, etc). Eu pensei: se o Google fez esse problema, ele deve ter bolado alguma solução genérica que independa de ifs. Bom, não que eu saiba. Depois de terminado o tempo dei uma olhada em algumas soluções dos competidores e não achei nenhuma solução que usasse algum algoritmo maluco e genérico (não achei nenhum indiano, contudo).

Eis a solução porca e mal-resolvida (alguns pontos do códido foram feitos depois de ver o código de outrem):

import sys
 
def FieldToString(field):
        ret = '\n'
	for r in field:
		for c in r:
			ret = ret + str(c)
		ret = ret + '\n'
	return ret
 
def CountMines(field, r, c):
        ret = 0
        row = len(field)
        col = len(field[0])
        if r < row-1 and field[r+1][c] == '*': ret = ret + 1
        if c < col-1 and field[r][c+1] == '*': ret = ret + 1
        if r > 0 and field[r-1][c] == '*': ret = ret + 1
        if c > 0 and field[r][c-1] == '*': ret = ret + 1
        if r < row-1 and c < col-1 and field[r+1][c+1] == '*': ret = ret + 1
        if r > 0 and col > 0 and field[r-1][c-1] == '*': ret = ret + 1
        if r < row-1 and c > 0 and field[r+1][c-1] == '*': ret = ret + 1
        if r > 0 and c < col-1 and field[r-1][c+1] == '*': ret = ret + 1
        return ret
 
def ExpandClick(field, r, c):
        if field[r][c] != '0': return
 
        def Expand(field, r, c):
                if field[r][c] == '.':
                        field[r][c] = str(CountMines(field, r, c))
                        ExpandClick(field, r, c)
 
        row = len(field)
        col = len(field[0])
        if r < row-1 and field[r+1][c] != '*':
                Expand(field, r+1, c)
        if c < col-1 and field[r][c+1] != '*':
                Expand(field, r, c+1)
        if r > 0 and field[r-1][c] != '*':
                Expand(field, r-1, c)
        if c > 0 and field[r][c-1] != '*':
                Expand(field, r, c-1)
        if r < row-1 and c < col-1 and field[r+1][c+1] != '*':
                Expand(field, r+1, c+1)
        if r > 0 and col > 0 and field[r-1][c-1] != '*':
                Expand(field, r-1, c-1)
        if r < row-1 and c > 0 and field[r+1][c-1] != '*':
                Expand(field, r+1, c-1)
        if r > 0 and c < col-1 and field[r-1][c+1] != '*':
                Expand(field, r-1, c+1)
 
def FieldClicker(field):
	row = len(field)
	col = len(field[0])
        for r in range(row):
                for c in range(col):
                        if field[r][c] == 'C':
                                field[r][c] = str(CountMines(field, r, c))
                                ExpandClick(field, r, c)
                                break
        return field
 
def FieldValidate(field):
        ret = True
	row = len(field)
	col = len(field[0])
        for r in range(row):
                for c in range(col):
                        if field[r][c] == '.':
                                ret = False
                                break
        return ret
 
def FieldRender(row, col, mines):
	field = []
        for i in range(row):
                field.append(['.'] * col)
        if row == 2:
                nextRow = 0
                nextCol = 0
                while mines:
                        field[nextRow][nextCol] = '*'
                        nextRow = nextRow + 1
                        if nextRow == row:
                                nextRow = 0
                                nextCol = nextCol + 1
                        mines = mines - 1
                field[0][col-1] = 'C'
        elif col == 2:
                nextRow = 0
                nextCol = 0
                while mines:
                        field[nextRow][nextCol] = '*'
                        nextCol = nextCol + 1
                        if nextCol == col:
                                nextRow = nextRow + 1
                                nextCol = 0
                        mines = mines - 1
                field[row-1][0] = 'C'
	elif row * col - mines < 3:
                nextRow = row - 1
                nextCol = 0
                while mines:
                        field[nextRow][nextCol] = '*'
                        nextCol = nextCol + 1
                        if nextCol == col:
                                nextRow = nextRow - 1
                                nextCol = 0
                        mines = mines - 1
                field[0][col-1] = 'C'
        else:
                for r in range(len(field)):
                        for c in range(len(field[0])):
                                field[r][c] = '*'
                if row * col - mines >= 9 and row >= 3 and col >= 3:
                        empties = row * col - mines
                        nextRow = 0
                        nextCol = 0
                        while empties:
                                
	return field
 
def Mine(row, col, mines):
        if row * col - mines == 2 and row > 1 and col > 1:
		return 'Impossible!'
	if row * col - mines == 3:
		return 'Impossible!'
	elif row * col - mines == 5:
		return 'Impossible!'
	elif row * col - mines == 7:
		return 'Impossible!'
	else:
		return FieldToString(FieldRender(row, col, mines))
 
f = open(sys.argv[1])
total = int(f.readline())
 
for case in range(1, total + 1):
        field = [int(i) for i in f.readline().split()]
        print 'Case #' + str(case) + ': ' + Mine(field[0], field[1], field[2])
 
 
 
#############################################################################3
 
def FieldRenderWrong(row, col, mines):
	field = []
	for i in range(row):
		field.append(['.'] * col)
 
        def GetNextRow(field, clickRow, clickCol):
                row = len(field)
                col = len(field[0])
                nextRow = 0
                nextCol = 0
                rowDist = 0
                colDist = 0
                for r in range(row):
                        for c in range(col):
                                if field[r][c] == '.':
                                        rDist = abs(r - clickRow)
                                        cDist = abs(c - clickCol)
                                        totDist = rDist + cDist
                                        currTotDist = rowDist + colDist
                                        if totDist > currTotDist:
                                                nextRow = r
                                                rowDist = rDist
                                                nextCol = c
                                                colDist = cDist
                                        else:
                                                rowCount = 0
                                                for r2 in range(row):
                                                        if field[r2][c] == '*':
                                                                rowCount = rowCount + 1
                                                colCount = 0
                                                for c2 in range(col):
                                                        if field[r][c2] == '*':
                                                                colCount = colCount + 1
                                                lastRow = rowCount == row - 1
                                                lastCol = colCount == col - 1
                                                if lastRow or lastCol:
                                                        nextRow = r
                                                        rowDist = rDist
                                                        nextCol = c
                                                        colDist = cDist
                return nextRow, nextCol
 
        clickRow = 0
        clickCol = col-1
	field[clickRow][clickCol] = 'C'
        nextRow, nextCol = GetNextRow(field, clickRow, clickCol)
	while mines:
		field[nextRow][nextCol] = '*'
		nextRow, nextCol = GetNextRow(field, clickRow, clickCol)
		mines = mines - 1
	return field

Não, eu não usei o Google para descobrir a lógica por trás do problema. Vai que os caras ficam monitorando quem fica fazendo pesquisas. E, não, tampouco usei o Bing. Não sou masoquista a esse ponto.

PS: Bom, estou na próxima fase. Veremos o que o futuro nos espera. Esse programador foi fisgado pelo campeonato de pastéis.

Ponto Flutuante Afundando (2)

Ponto Flutuante Afundando

8

Quando armazenamos valores monetários em doubles seus cálculos conseguem manter a precisão e na maioria das vezes o ajuste de precisão funciona. Porém, encontrei alguns casos onde a subtração de dois valores fazia "perder" um centavo (ou comparações exatas) justamente pela limitação da precisão do ponto flutuante. Nesse exemplo os valores são 2.358,93 - 1.386,93, que em uma conta de padaria (mas correta) dá 972,00 (até a Calc do Windows e o Excel funcionam), mas pelo Visual Studio 2010 chega perto, mas erra o alvo:

#include <iostream>
 
int main()
{
	double d1 = 2358.93;
	double d2 = 1386.93;
	double d3 = d1 - d2;
 
	std::cout << "d1: " << d1 << "\n";
	std::cout << "d2: " << d2 << "\n";
	std::cout << "d1 - d2 = 3d: " << d3 << "\n";
 
	// comparando armazenamentos que diferem
	std::cout << "d3 == 972.0: " << std::boolalpha << ( d3 == 972.0 ) << "\n";
 
	// comparando armazenamentos similares
	std::cout << "d1 == 2358.93: " << std::boolalpha << ( d1 == 2358.93 ) << "\n";
	std::cout << "d2 == 1386.93: " << std::boolalpha << ( d2 == 1386.93 ) << "\n";
}

Isso ocorre porque sua representação dentro da variável double é diferente de 272.0 do outro double. Depurando vemos mais claramente:

Ponto Flutuante Afundando

Ou seja, quando fazemos a subtração de d2 em d1, nossa precisão raspa um pouquinho e escapa pela beirada:

d1 2358.9299999999998
d2 1386.9300000000001
======================
d3 971.999999999999777
||||||
Esse é o valor "desejado".

Na comparação com o valor redondo aparece a falha, mas note que isso não ocorre com os outros valores d1 e d2, já que o armazenamento adquire o mesmo formato:

Ponto Flutuante Afundando (2)

Corrigindo o incorrigível

Há uma forma de arredondamento já disponível no C99 (mas não no Visual Studio 2010) que pode ser útil para esses casos. A única coisa que é preciso fazer é arredondar os valores antes do cálculo:

#include <iostream>
 
double round(double r)
{
    return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}
 
int main()
{
	double d1 = 2358.93;
	double d2 = 1386.93;
	double d3 = round(d1) - round(d2);
 
	std::cout << "d1: " << d1 << "\n";
	std::cout << "d2: " << d2 << "\n";
	std::cout << "d1 - d2 = 3d: " << d3 << "\n";
	std::cout << "d3 == 972.0: " << std::boolalpha << ( d3 == 972.0 ) << "\n";
}

É uma decisão arbitrária essa de arredondar para cima, mas se for adotada em todo o sistema (e já fazendo parte de um padrão, no caso, o C99), não deverão existir problemas de interpretação de cálculos entre os componentes.

O mercado financeiro agradece =).

UPDATE

Não estou de acordo com o armazenamento de valores monetários em doubles em vez de inteiros pelo simples motivo que não há moedas no sistema financeiro com unidades que se dividem ad infinitum. Por consequência, existe sempre uma unidade básica e indivisível (que no caso do Brasil é o centavo de real). Ou seja, como o objetivo é contar o total dessas unidades que não se dividem, o uso de inteiros é brainless.

UPDATE 2

Existe uma discussão exatamente sobre isso no Grupo C/C++ Brasil, que recomendo a leitura, o que me levou a escrever o post. Particularmente, sigo o raciocínio do Pedro Lamarão.

Palestra TDC2012

Minha palestra do TDC 2012

4

Duas semanas atrás rolou a trilha C++ do TDC 2012, que contou com além da minha presença com a dos já conhecidos Fernando Roberto (DriverEntry), Rodrigo Strauss (1Bit), etc. Uma novidade: meu colega e programador .nerd Gabriel Guilherme também participou em uma palestra sobre um assunto que acredito que deveria ser mais promovido: interop. Afinal de contas, o poder de C++ não seria nada se não houvesse motivos práticos para usá-lo. Entre esses motivos, construir soluções com linguagens mais acessíveis é um deles.

Na minha palestra foquei no conteúdo dos meus dois artigos sobre um fictício Patch de Emergência (parte 1 e parte 2). No entanto, para agregar um pouco de valor ao conteúdo, estendi os exemplos para fornecer os caminhos para o desenvolvimento de uma solução profissional e usável de um quem-sabe real patch desses (ouvi falar que empresas como Microsoft utilizam isso).

Patch de Emergencia from Wanderley Caloni

Os fontes e a apresentação se encontram no meu já conhecido branch de exemplos no GitHub, mas para quem não sabe do que eu estou falando segue um linque com um pacote 7zip.

Meus repositórios no GitHub

0

Depois de vacilar por alguns meses, incentivado pelo meu amigo Chico Pimenta, resolvi experimentar o tal do GitHub, e consequentemente o sistema de controle de fontes distribuído Git, que antes era meio exclusivo do Linux (continua meio sendo, mas com suporte um pouco melhor para Windows).

Com isso, dei uma pequena lida no livro de introdução e comecei a migrar meus fontes perdidos num canto do HD. O que notei de vantagem com relação a outros DRCSs foi que é muito fácil e rápido criar branches e que a comunicação remota e os commits são feitos de uma maneira mais organizada e estruturada, além da própria estrutura interna do repositório ser muito simples de entender: um bando de arquivos compactados cujo nome é o hash do que ele contém.

Meus  repositórios estão armazenados em alguns branches que distribuí de acordo com o uso/importância:

  • OpenSource. Projetos de fonte aberto que mantenho/ive e que poderiam se perder se alguém não fizesse backup (como o mouse tool ou regmon).
  • Samples. Códigos de exemplo, de palestras e de testes feitos para escrever os artigos do blogue cujo autor vos fala.
  • Caloni. Os códigos que fazem algo de útil, como o Houaiss2Babyulon, CopiaExata e DayToDay.
  • Book. Um projeto em estado de larva sobre escrever um livro de engenharia reversa. Já possui um índice básico. Sugestões são bem-vindas.
  • DriverEntry. Códigos do curso de desenvolvimento de drivers que estou fazendo com o Fernando, da DriverEntry Company. Recomendo!
tagcloud

Conteúdo da Palestra

0

Uma nuvem mágica...

O evento de C++ na Microsoft (e organizada pelo nosso grupo C/C++ Brasil) aconteceu. Quem esteve lá teve o privilégio de passar algumas horas com programadores e entusiastas de ambas as linguagens e acompanhar o raciocínio dos palestrantes sobre Move Semantics, COM (good times), programação na placa gráfica e a solução para todos os problemas do universo (vulgo ZeroMQ). Fora isso, a palestra que me surpreendeu no dia foi a do Sr. Basílio Miranda, cômica e inspirada, mas sempre nos fornecendo um pouquinho mais de conhecimento acerca do Qt framework, que felizmente ainda não morreu (a Nokia recentemente liberou uma série nova que ainda usa a versão evoluída do Symbian), e graças a isso não precisaremos nos preocupar por enquanto com o destino de nosso ilustríssimo Sr. Basílio.

Fora isso tivemos uma telepalestra com um dos membro do time do Visual Studio. Mas, francamente, estou cada vez menos interessado no VS e mais no Vim. Portanto...

Segue minha palestra e meu código-fonte sobre Move Semantics devidamente compactados para apreciação dos interessados. Comentários, sugestões, dicas são sempre bem-vindos, como bem sabem os que me procuram eventualmente através de e-mails ou no blogue.

Peixe Gelatinoso Bizarro do Brasil (conhece esse país?)

FormatMessage para… dumies?

0

Peixe Gelatinoso Bizarro do Brasil (conhece esse país?)

Já foi comentado em alguns círculos de ótimos programadores que a função da Win32 API FormatMessage é uma das criaturas mais bizarras já criadas.

O objetivo da FormatMessage é formatar uma string, assim como sprintf, mas voltado mais a escrever uma descrição de um código de erro. Sendo assim ela é essencial para que o usuário não receba um número no lugar de uma explicação de por que a operação falhou.

Os códigos de erro que ela se propõe a formatar podem ser os erros padrões descritos em winerror.h ou qualquer outro código cuja explicação esteja em algum módulo carregado pelo processo (DLL ou o próprio executável). Isso nos dá a liberdade de, por exemplo, criar uma DLL apenas com códigos e descrições dos erros dos nossos produtos.

Para que seja criada a mensagem final, uma definição de mensagem é requirida como entrada, que pode vir do próprio chamador ou da já mencionada tabela de erros de algum módulo qualquer. No caso de querermos a descrição de um erro de sistema (em winerror.h, retornado por GetLastError ou similares) a definição da mensagem já está embutida no sistema, bastando para nós passarmos o código.

É importante lembrar que, como estamos falando de uma descrição de erro, ou seja, de um texto, este pode vir em diversos idiomas, sendo que é nossa obrigação também definir para qual idioma desejamos traduzir nosso código de erro, sendo também nossa obrigação, no caso de mensagens específicas do nosso programa, fornecer o modelo da mensagem nos idiomas que formos suportar.

O resto da função funciona mais ou menos como o sprintf, cuspindo a mensagem-modelo em uma saída formatada de acordo com os parâmetros de entrada.

(more...)

Arrastar-e-soltar controles do Windows

Três em um

2

Que vergonha passar tanto tempo sem postar nada. Parece que não fiz nada que valesse a pena comentar por aqui.

Na verdade, não fiz tanto, mesmo. Muitas mensagens do Outlook, gráficos UML e reuniões de alinhamento depois, sobrou um tempinho pra programar. Aprendi algumas coisas que tinha o desejo de saber há tanto tempo... Agora eu sei, quem diria, criar linques suspensos nas janelas Win32! Que novidade, não? Pois é, isso exige, de acordo com o SDK, algumas artimanhas pra fazer funcionar. Para quem está de Visual Studio 2008/2010 na mão basta seguir os passos seguintes.

(more...)

Threads Room

A sala da fila das threads

1

Quando falei sobre a fila das threads, e como cada thread espera pacientemente em uma fila até chegar sua vez de ser atendida no guichê das CPUs, também vimos como é fácil fazer caquinhas em um programa que roda paralelamente duas threads ou mais.

Também falei que iríamos resolver esse problema, afinal de contas, temos que salvar todos aqueles programas que usam dezenas de threads trabalhando ao mesmo tempo para contar números de um até dez.

A boa notícia é que o salvamento é mais simples do que parece: coloque todas as suas threads em uma sala trancada e deixe apenas uma chave. As threads terão que brigar para sair da sala e, depois que a vencedora sair, as outras terão que ficar esperando ela voltar.

Confuso? Se estiver, ainda bem. Isso quer dizer que estamos novamente em um daqueles artigos com "pseudo-parábolas", a maneira mais ilustrada de explicar as coisas.

(more...)

Fila das threads

A fila das threads

2

Telling Stories Em um ambiente multithreading, diversas threads disputam "a tapas" a atenção do processador (CPU). Certo? Podemos dizer que, em um ambiente com muito processamento a realizar, de certa forma é isso que acontece. São threads e mais threads rodando um pedacinho de código cada vez que passam pelo processador.

Um ambiente complexo como esse é repleto de pequenos detalhes que podem fazer o iniciante logo desanimar quando tentar depurar um programa com mais de uma thread. De fato, eu já percebi que muitos não vão saber nem como começar a mastigar o problema.

Por isso mesmo que eu tive que inventar algumas analogias no mínimo interessantes a respeito do assunto. Sem analogias, não sei como falaria sobre essas coisas de forma amena e "explicável".

A primeira "parábola" conta a história da fila das threads em direção ao guichê das CPUs.

O exemplo abaixo cria três threads, todas iniciando na mesma função. O objetivo de todas elas é incrementar um contador até que seu valor chegue a 10. Todas param quando esse objetivo é alcançado.

(more...)

job.gif

Windows Jobs com Completion Port

0

Ou "Como esperar o término de todos os processos-filho criados a partir de um conjunto de processos".

Dessa vez confesso que esperava um pouco mais de documentação do MSDN, ou pelo menos um sistema de referências cruzadas eficiente. Outro dia demorei cerca de duas horas para conseguir criar um job, anexar o processo desejado e, a pior parte, esperar que todos os processos (o principal e seus filhos e netos) terminassem.

Além da pouca documentação, parece que não são muitas as pessoas que fazem isso e publicam na web, ou eu não sei procurar direito.

Mas, pra início de conversa, o que é um job mesmo?

(more...)

Go to Top