Vejo que a pergunta foi reativada com generosidade, perguntando agora quais são os usos práticos yield
. Vou dar um exemplo de minha experiência.
Como sabemos, yield
força o thread de chamada a desistir do processador no qual está sendo executado para que outro thread possa ser agendado para ser executado. Isso é útil quando o encadeamento atual concluiu seu trabalho por enquanto, mas deseja retornar rapidamente para o início da fila e verificar se alguma condição mudou. Como isso é diferente de uma variável de condição? yield
permite que o thread retorne muito mais rápido ao estado de execução. Ao esperar por uma variável de condição, o encadeamento é suspenso e precisa aguardar um encadeamento diferente para sinalizar que deve continuar.yield
basicamente diz "permitir a execução de um thread diferente, mas permita-me voltar ao trabalho logo, pois espero que algo mude em meu estado muito rapidamente". Isso sugere uma rotação ocupada, em que uma condição pode mudar rapidamente, mas suspender o encadeamento incorreria em um grande impacto no desempenho.
Mas chega de tagarelice, aqui está um exemplo concreto: o padrão paralelo da frente de onda. Uma instância básica desse problema é calcular as "ilhas" individuais de 1s em uma matriz bidimensional preenchida com 0s e 1s. Uma "ilha" é um grupo de células adjacentes umas às outras, vertical ou horizontalmente:
1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1
Aqui temos duas ilhas de 1s: superior esquerdo e inferior direito.
Uma solução simples é fazer uma primeira passagem por toda a matriz e substituir os valores 1 por um contador incremental de forma que no final cada 1 seja substituído por seu número de sequência na ordem principal da linha:
1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8
Na próxima etapa, cada valor é substituído pelo mínimo entre ele e os valores de seus vizinhos:
1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4
Agora podemos facilmente determinar que temos duas ilhas.
A parte que queremos executar em paralelo é a etapa em que calculamos os mínimos. Sem entrar em muitos detalhes, cada thread obtém linhas de uma maneira intercalada e depende dos valores calculados pelo thread que processa a linha acima. Assim, cada encadeamento precisa ficar ligeiramente para trás do encadeamento que processa a linha anterior, mas também deve se manter dentro de um tempo razoável. Mais detalhes e uma implementação são apresentados por mim neste documento . Observe o uso de sleep(0)
que é mais ou menos equivalente a C de yield
.
Nesse caso, yield
foi usado para forçar cada thread a pausar, mas como o thread que processa a linha adjacente avançaria muito rapidamente nesse meio tempo, uma variável de condição seria uma escolha desastrosa.
Como você pode ver, yield
é uma otimização bastante refinada. Usá-lo no lugar errado, por exemplo, aguardar uma condição que muda raramente, causará uso excessivo da CPU.
Desculpe pela longa tagarelice, espero ter sido claro.