प्रति सेकंड एक बार लूप चलाना


33

मैं हर पल कुछ चीजों को जांचने और छापने के लिए इस लूप को चला रहा हूं। हालाँकि, क्योंकि गणना में शायद कुछ सौ मिलीसेकंड लगते हैं, मुद्रित समय कभी-कभी एक सेकंड को छोड़ देता है।

क्या इस तरह के लूप को लिखने का कोई तरीका है जिसे मैं हर सेकंड प्रिंटआउट प्राप्त करने की गारंटी देता हूं? (बशर्ते, निश्चित रूप से, कि लूप में गणना एक दूसरे से कम लगती है :))

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


26
ध्यान दें कि " ठीक एक बार प्रति सेकंड" ज्यादातर मामलों में शाब्दिक रूप से संभव नहीं है क्योंकि आप (आमतौर पर) यूजर्सस्पेस में चल रहे हैं जो कि एक प्रीमेप्टिकली मल्टीटास्किंग कर्नेल के ऊपर चल रहा है जो आपके कोड को शेड्यूल करेगा क्योंकि यह फिट दिखता है (ताकि आप नींद के बाद नियंत्रण वापस न पा सकें। समाप्त होता है, उदाहरण के लिए)। जब तक आप C कोड नहीं लिख रहे हैं जो sched(7)API (POSIX: see <sched.h>और वहां से जुड़े पेज) में कॉल करता है , तो आप मूल रूप से इस फॉर्म की वास्तविक समय की गारंटी नहीं ले सकते।
केविन

बस @ केविन ने क्या कहा, नींद का उपयोग करने के लिए () कोशिश करने और सटीक समय पाने के लिए किसी भी तरह की विफलता को बर्बाद किया जाता है, यह केवल नींद की अति कम से कम 1 सेकंड की गारंटी देता है। यदि आपको वास्तव में सटीक समय की आवश्यकता है, तो आपको सिस्टम घड़ी (CLOCK_MONOTONIC देखें) और समय-अंतिम-घटना + 1 s के आधार पर चीजों को ट्रिगर करने की आवश्यकता है, और सुनिश्चित करें कि आप> 1sec चलाने के लिए अपने आप को यात्रा न करें अगली बार कुछ ऑपरेशन आदि के बाद गणना करना
जॉन यू

सिर्फ इस यहाँ छोड़ने के लिए जा रहा falsehoodsabouttime.com
Malbarez आलो

संक्षेप में एक बार एक दूसरे = एक VCXO का उपयोग करें। एक सॉफ़्टवेयर-केवल समाधान आपको केवल "अच्छा पर्याप्त" मिलेगा, लेकिन सटीक नहीं।
इयान मैकडोनाल्ड

जवाबों:


65

मूल कोड से थोड़ा सा दूर रहने के लिए, मैं क्या करूँ:

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

यह शब्दार्थ को थोड़ा बदल देता है: यदि आपका सामान एक सेकंड से भी कम समय लेता है, तो यह पूरी तरह से दूसरा पास होने का इंतजार करेगा। हालांकि, यदि आपका सामान किसी भी कारण से एक सेकंड से अधिक समय लेता है, तो यह कभी भी किसी भी अंत के साथ अधिक सबप्रोसेस को पैदा नहीं करेगा।

इसलिए आपका सामान कभी भी समानांतर में नहीं चलता है, और पृष्ठभूमि में नहीं होता है, इसलिए चर भी अपेक्षा के अनुरूप काम करते हैं।

ध्यान दें कि यदि आप अतिरिक्त पृष्ठभूमि कार्य शुरू करते हैं, तो आपको विशेष रूप waitसे sleepप्रक्रिया की प्रतीक्षा करने के लिए निर्देश को बदलना होगा ।

यदि आपको इसे और भी सटीक बनाने की आवश्यकता है, तो आपको शायद इसे सिस्टम घड़ी में सिंक करना होगा और एमएस को पूरे सेकंड के बजाय सो जाना होगा।


सिस्टम क्लॉक से सिंक कैसे करें? कोई विचार नहीं वास्तव में, बेवकूफ प्रयास:

चूक:

while sleep 1
do
    date +%N
done

आउटपुट: 003511461 010510925 016081282 021643477 028504349 03 ... (रहता है)

सिंक किया गया:

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

आउटपुट: 002648691 001098397 002514348 001293023 001679137 00 ... (वही रहें)


9
यह नींद / प्रतीक्षा चाल वास्तव में चतुर है!
फिल्हाल

मैं सोच रहा हूँ कि क्या सभी कार्यान्वयन sleepभिन्नात्मक सेकंड को संभालेंगे?
jcaron

1
@jcaron उन सभी को नहीं। लेकिन यह गन्नो नींद और बिजीबॉक्स नींद के लिए काम करता है, इसलिए यह विदेशी नहीं है। आप शायद एक साधारण वापसी कर सकते हैं जैसे sleep 0.9 || sleep 1कि अमान्य पैरामीटर नींद के कभी भी विफल होने का एकमात्र कारण है।
फ्रॉन्स्चट्ज़

@frostschutz मुझे लगता है कि भोले कार्यान्वयन के sleep 0.9रूप में व्याख्या की जाएगी sleep 0(यह देखते हुए कि क्या atoiकरना होगा)। यकीन नहीं है कि अगर वास्तव में एक त्रुटि होगी।
जकरॉन

1
मुझे यह देखकर खुशी हुई कि इस सवाल ने बहुत दिलचस्पी पैदा की। आपका सुझाव और उत्तर बहुत अच्छा है। न केवल यह दूसरे के भीतर रखता है, बल्कि यह संभव के रूप में पूरे दूसरे के करीब भी चिपक जाता है। प्रभावशाली! (पुनश्च! एक तरफ ध्यान दें, एक को GNU Coreutils स्थापित करना चाहिए और कार्य gdateकरने के लिए macOS पर उपयोग करना चाहिए date +%N।)
१२'१rin

30

यदि आप अपने लूप को स्क्रिप्ट / ऑनलाइनर में रिस्ट्रिक्ट कर सकते हैं तो ऐसा करने का सबसे सरल तरीका watchऔर इसका preciseविकल्प है।

आप watch -n 1 sleep 0.5इसके साथ प्रभाव देख सकते हैं - यह कुछ सेकंड तक गिनती दिखाएगा, लेकिन कभी-कभी एक सेकंड में छोड़ देगा। इसे watch -n 1 -p sleep 0.5प्रति सेकंड दो बार आउटपुट के रूप में चलाना , और आपको कोई भी स्काइप दिखाई नहीं देगा।


11

एक बैकग्राउंड जॉब के रूप में चलने वाले सबस्क्रिप्शन में ऑपरेशंस चलाना उन्हें इतना परेशान नहीं करेगा 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

केवल एक सेकंड से "चुराया गया" समय सबस्क्रिप्शन को लॉन्च करने के लिए लिया गया समय होगा, इसलिए यह अंततः एक सेकंड को छोड़ देगा, लेकिन मूल कोड की तुलना में कम अक्सर।

यदि उप-कोड में कोड एक से अधिक सेकंड का उपयोग करके समाप्त होता है , तो लूप पृष्ठभूमि की नौकरियों को जमा करना शुरू कर देगा और अंततः संसाधनों से बाहर चला जाएगा।


9

एक अन्य विकल्प (यदि आप उपयोग नहीं कर सकते हैं, उदाहरण के लिए, watch -pजैसा कि Maelstrom से पता चलता है) sleepenh[ मैनपेज ] है, जो इसके लिए डिज़ाइन किया गया है।

उदाहरण:

#!/bin/sh

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

sleep 0.2वहाँ ध्यान दें कि कुछ समय लेने वाला कार्य लगभग 200ms में कर रहा है। इसके बावजूद, नैनोसेकंड का उत्पादन स्थिर रहता है (अच्छी तरह से, गैर-रीयल टाइम ओएस मानकों के अनुसार) - यह प्रति सेकंड एक बार होता है:

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

यह 1ms अलग है, और कोई प्रवृत्ति नहीं है। यह काफी अच्छा है; यदि सिस्टम पर कोई लोड है - तो आपको कम से कम 10ms के उछाल की उम्मीद करनी चाहिए - लेकिन फिर भी समय के साथ कोई बहाव नहीं होता है। यानी, आप एक सेकंड भी नहीं गंवाएंगे।


7

के साथ zsh:

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

आपकी नींद चल बिन्दु सेकंड का समर्थन नहीं करता, तो आप उपयोग कर सकते हैं zsh's zselectबजाय (एक के बाद 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

जब तक लूप में कमांड को चलाने में एक सेकंड से कम समय न लगे, तब तक उन्हें बहाव नहीं करना चाहिए।


0

मुझे POSIX शेल स्क्रिप्ट के लिए बिल्कुल वही आवश्यकता थी, जहां सभी हेल्पर्स (usleep, GNUsleep, sleepenh, ...) उपलब्ध नहीं हैं।

देखें: 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.