paulo1205
(usa Ubuntu)
Enviado em 20/04/2016 - 09:49h
O jeito indicado pelo listeiro_037 resolve PARCIALMENTE para um loop de espera ocupada. Entretanto, a não ser que exista alguma sólida razão para uma espera ocupada, que gasta 100% de CPU em pelo menos um core (com gasto de energia, principalmente se for bateria de um laptop ou dispositivo móvel), é quase sempre melhor usar espera simples, sob controle do sistema operacional (e, portanto, não portátil para sistemas operacionais diferentes), que não consuma CPU.
Mas por que o código indicado é apenas uma solução parcial? Por várias razões, entre as quais:
- Há que se lembrar que nem todo hardware para o qual exista uma implementação da linguagem C dispõe de relógio.
- Até mesmo por causa disso, o padrão do C especifica a função
clock () e a constante
CLOCKS_PER_SEC , mas não descreve seu funcionamento com precisão. Uma implementação possível e conforme o padrão poderia ter
CLOCKS_PER_SEC==1 e uma função
clock () que incrementasse o contador cada vez que fosse chamada, independentemente do tempo decorrido ou da quantidade de CPU consumida.
- A função
clock () não mede tempo decorrido, mas sim o tempo em que o processador ficou ocupado com o programa. Assim sendo, numa máquina single-core (ou mesmo numa multi-core, mas muito ocupada), em que o programa dispute CPU com outros processos, provavelmente o programa iria medir intervalos diferentes do que se poderia medir com um cronômetro externo. Colocando de outra forma, não se poderia usar essa função para implementar um cronômetro de eventos externos ao programa. Ou, ainda, ilustrando com um exemplo: se o programa dividir a CPU equitativamente com quatro outros processos, e se pedir um atraso de 10 segundos, pode ser que ele leve 50 segundos para dar esse atraso por encerrado.
- Outro efeito de medir o tempo de CPU gasto, e não o tempo decorrido, é que se o programa tiver a execução temporariamente suspensa (como por efeito de
sleep () (UNIX),
Sleep () (Windows) ou algum gargalo de I/O), o tempo não vai ser incrementado. Por exemplo, o programa abaixo, para UNIX, vai produzir um valor ridiculamente baixo, embora demore a executar.
#include <stdio.h>
#include <time.h>
#include <unistd.h>
int main(void){
clock_t t0=clock();
sleep(10); // suspende execução do processo por 10 segundos
printf("Tempo medido com clock(): %0.6f\n", (double)(clock()-t0)/CLOCKS_PER_SEC);
return 0;
}
- A representação interna do contador, feita com um inteiro, pode sofrer
overflow com
wrap-around a qualquer momento (pois não é garantido que comece com zero), e também periodicamente. Com
CLOCKS_PER_SEC==1000000 , como costuma ser no mundo UNIX, e com uma representação interna com inteiros de 32 bits, o relógio “vira” a cada 72 minutos (71min34.967296s), inviabilizando qualquer contagem de tempo maior do que esse. Outras plataformas e sistemas podem ter restrições ainda piores.
Além disso, o nome dado à função pode ser problemático:
pause () é o nome de uma chamada ao sistema bem definida no mundo UNIX.
Por fim, e apesar de tudo, eu tenho uma sugestão de “otimização” (possivelmente não muito significativa quando já se tem uma espera ocupada, mas algo que é bom deixar “no sangue”, para usar em construções semelhantes em outras situações) ao programa do listeiro_037: em vez de, a cada iteração, subtrair o tempo final (sujeito ainda por cima a uma divisão de ponto flutuante) do inicial e comparar a subtração com o intervalo, pode-se somar, antes do início do loop, o intervalo ao tempo inicial, escalado para clock_t por meio de multiplicação (em vez de divisão), para ter o valor do tempo final, e comparar simplesmente o instante lido a cada iteração com essa hora final. Essa mudança deixaria o algoritmo com menos operações, as operações usadas seriam mais simples e eficientes, haveria menos variáveis, e ainda reduz bastante (embora não elimine) o risco de ter um loop que nunca termina.
Em outras palavras, usar, em lugar de algo como
void delay(float intervalo){
if(intervalo<INTERVALO_MINIMO)
return;
float t0=(float)clock()/CLOCKS_PER_SEC;
float t1=(float)clock()/CLOCKS_PER_SEC;
/*
Note que o loop abaixo pode nunca acabar, se o relógio “virar”
num período menor que o intervalo solicitado.
*/
while(t1-t0<intervalo)
t1=(float)clock()/CLOCKS_PER_SEC;
}
fazer mais ou menos o seguinte.
void delay(float intervalo){
clock_t clk_final=clock()+intervalo*CLOCKS_PER_SEC;
/*
Note que o loop abaixo pode ter duração indefinida se
intervalo*CLOCKS_PER_SEC provocar wrap-around,
mas o loop tem mais chances de chegar ao fim (maiores
chances quanto mais longe clk_final estiver do máximo
valor representável).
*/
while(clock()<clk_final)
; /* Corpo do while é um comando vazio. */
}