AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 460836
Accepted
forthrin
forthrin
Asked: 2018-08-07 06:57:35 +0800 CST2018-08-07 06:57:35 +0800 CST 2018-08-07 06:57:35 +0800 CST

Executando um loop precisamente uma vez por segundo

  • 772

Estou executando este loop para verificar e imprimir algumas coisas a cada segundo. No entanto, como os cálculos levam talvez algumas centenas de milissegundos, o tempo impresso às vezes salta um segundo.

Existe alguma maneira de escrever um loop que tenha a garantia de obter uma impressão a cada segundo? (Desde que, é claro, os cálculos no loop levem menos de um segundo :))

while true; do
  TIME=$(date +%H:%M:%S)
  # some calculations which take a few hundred milliseconds
  FOO=...
  BAR=...
  printf '%s  %s  %s\n' $TIME $FOO $BAR
  sleep 1
done
bash timestamps
  • 6 6 respostas
  • 13592 Views

6 respostas

  • Voted
  1. Best Answer
    frostschutz
    2018-08-07T07:20:14+08:002018-08-07T07:20:14+08:00

    Para ficar um pouco mais próximo do código original, o que eu faço é:

    while true; do
      sleep 1 &
      ...your stuff here...
      wait # for sleep
    done
    

    Isso muda um pouco a semântica: se o seu material demorar menos de um segundo, ele simplesmente aguardará o segundo inteiro passar. No entanto, se o seu material demorar mais de um segundo por qualquer motivo, ele não continuará gerando ainda mais subprocessos sem nunca terminar.

    Portanto, suas coisas nunca são executadas em paralelo, e não em segundo plano, portanto, as variáveis ​​também funcionam conforme o esperado.

    Observe que, se você também iniciar tarefas adicionais em segundo plano, precisará alterar a waitinstrução para aguardar apenas o sleepprocesso especificamente.

    Se você precisar que ele seja ainda mais preciso, provavelmente precisará sincronizá-lo com o relógio do sistema e dormir ms em vez de segundos completos.


    Como sincronizar com o relógio do sistema? Não faço ideia, tentativa estúpida:

    Predefinição:

    while sleep 1
    do
        date +%N
    done
    

    Saída: 003511461 010510925 016081282 021643477 028504349 03... (continua crescendo)

    Sincronizado:

     while sleep 0.$((1999999999 - 1$(date +%N)))
     do
         date +%N
     done
    

    Saída: 002648691 001098397 002514348 001293023 001679137 00... (permanece igual)

    • 67
  2. Maelstrom
    2018-08-07T16:10:35+08:002018-08-07T16:10:35+08:00

    Se você puder reestruturar seu loop em um script / oneliner, a maneira mais simples de fazer isso é com watche sua preciseopção.

    Você pode ver o efeito com watch -n 1 sleep 0.5- ele mostrará a contagem de segundos, mas ocasionalmente pulará um segundo. Executá-lo como watch -n 1 -p sleep 0.5produzirá duas vezes por segundo, a cada segundo, e você não verá nenhum pulo.

    • 30
  3. Kusalananda
    2018-08-07T07:02:51+08:002018-08-07T07:02:51+08:00

    Executar as operações em um subshell que é executado como um trabalho em segundo plano faria com que elas não interferissem tanto com o sleep.

    while true; do
      (
        TIME=$(date +%T)
        # some calculations which take a few hundred milliseconds
        FOO=...
        BAR=...
        printf '%s  %s  %s\n' "$TIME" "$FOO" "$BAR"
      ) &
      sleep 1
    done
    

    O único tempo "roubado" de um segundo seria o tempo necessário para iniciar o subshell, então ele eventualmente pularia um segundo, mas com menos frequência do que o código original.

    Se o código no subshell usar mais de um segundo, o loop começará a acumular trabalhos em segundo plano e, eventualmente, ficar sem recursos.

    • 11
  4. derobert
    2018-08-08T09:41:00+08:002018-08-08T09:41:00+08:00

    Outra alternativa (se você não pode usar, por exemplo, watch -pcomo Maelstrom sugere) é sleepenh[ manpage ], que é projetado para isso.

    Exemplo:

    #!/bin/sh
    
    t=$(sleepenh 0)
    while true; do
            date +'sec=%s ns=%N'
            sleep 0.2
            t=$(sleepenh $t 1)
    done
    

    Observe a sleep 0.2simulação fazendo alguma tarefa demorada consumindo cerca de 200ms. Apesar disso, a saída de nanossegundos permanece estável (bem, pelos padrões do sistema operacional não em tempo real) - isso acontece uma vez por segundo:

    sec=1533663406 ns=840039402
    sec=1533663407 ns=840105387
    sec=1533663408 ns=840380678
    sec=1533663409 ns=840175397
    sec=1533663410 ns=840132883
    sec=1533663411 ns=840263150
    sec=1533663412 ns=840246082
    sec=1533663413 ns=840259567
    sec=1533663414 ns=840066687
    

    Isso é menos de 1ms diferente e nenhuma tendência. Isso é muito bom; você deve esperar saltos de pelo menos 10 ms se houver alguma carga no sistema - mas ainda sem desvios ao longo do tempo. Ou seja, você não vai perder um segundo.

    • 9
  5. Stéphane Chazelas
    2018-08-07T22:42:48+08:002018-08-07T22:42:48+08:00

    Com zsh:

    n=0
    typeset -F SECONDS=0
    while true; do
      date '+%FT%T.%2N%z'
      ((++n > SECONDS)) && sleep $((n - SECONDS))
    done
    

    Se o seu sono não suportar segundos de ponto flutuante, você pode usar zsh's zselect(depois de um zmodload zsh/zselect):

    zmodload zsh/zselect
    n=0
    typeset -F SECONDS=0
    while true; do
      date '+%FZ%T.%2N%z'
      ((++n > SECONDS)) && zselect -t $((((n - SECONDS) * 100) | 0))
    done
    

    Eles não devem se deslocar enquanto os comandos no loop levarem menos de um segundo para serem executados.

    • 7
  6. Bastian Bittorf
    2019-03-25T02:47:01+08:002019-03-25T02:47:01+08:00

    Eu tinha exatamente o mesmo requisito para um script de shell POSIX, onde todos os auxiliares (usleep, GNUsleep, sleepenh, ...) não estão disponíveis.

    veja: https://stackoverflow.com/a/54494216

    #!/bin/sh
    
    get_up()
    {
            read -r UP REST </proc/uptime
            export UP=${UP%.*}${UP#*.}
    }
    
    wait_till_1sec_is_full()
    {
        while true; do
            get_up
            test $((UP-START)) -ge 100 && break
        done
    }
    
    while true; do
        get_up; START=$UP
    
        your_code
    
        wait_till_1sec_is_full
    done
    
    • 1

relate perguntas

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    ssh Não é possível negociar: "nenhuma cifra correspondente encontrada", está rejeitando o cbc

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    Como descarregar o módulo do kernel 'nvidia-drm'?

    • 13 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add retorna com: "Erro ao conectar ao agente: nenhum arquivo ou diretório" 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve