Використання циклу для запуску моєї програми в новій темі - не використовуйте сон?

У мене просто є програма, яка виконувала це в своїй власній темі:

public void run(){
    long lastTime = System.nanoTime();
    float lastSleep = 0;
    //Everything is in seconds.
    while(running){
        float delta = (System.nanoTime()-lastTime)/1000000000f;
        lastTime = System.nanoTime();
        manager.update(delta);
        panel.repaint();
        lastSleep = Math.max(maxTicSpeed-(delta-lastSleep),5/1000f);
        try{
            Thread.sleep((long) Math.round(lastSleep*1000));
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

В принципі, я завжди навчився спати, коли циклічно, як це, так я і зробив, моя програма спит не менше 5 мілісекунд, або найбільше часу вона може спати без перевищення ліміту (1/30 секунди). Але я читав навколо, і сон не звучить, як це дуже добре.

http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx

Від того, що він каже, це звучить так, як моя програма навіть не спатиме, якщо вона буде надто близько до нижньої межі сну, тощо. Коли system.printing зміни в часі зміна коливається приблизно з .31508 - .03475, що насправді досить добре для мене, як моя програма рахунку за неточності.

Що сказано, що я можу зробити замість цього? Я вирішив додати щось на зразок цього, а не спробувати (Sleep):

long waitTill = (long) (System.nanoTime()+lastTime/1000000000f), 
    now = System.nanoTime();
while(now < waitTill){
    now = System.nanoTime();
}

Але чи не моя нитка все-таки займатиме таку ж кількість процесорного часу? Я думав, що справа полягає в тому, щоб зупинити нашу нитку від зайняття більшої кількості процесорів, ніж це потрібно насправді.

Отже, чи слід використовувати сон (при більшому мінімальному часу сну?), Чи повинен я використовувати свою альтернативу, чи повинен я використовувати іншу альтернативу, чи повинен я просто дозволити моєму циклу програми з нестримною швидкістю? Я погано програмую, навіть якщо враховую неточність сну?

Дякую за допомогу!

EDIT: So, Timers have been recommened, however I understand that if my task didn't finish before the Timer called again then I would run into issues. That is a definate concern with my program. I feel like I have dealt with the problem of Thread.sleep() by using a delta, so, would Thread.sleep() as before be better?

2
Я оновлю свою відповідь, щоб поговорити про вашу редагування. Я все ще стверджую, що підхід до запланованого завдання закінчується більш надійним рішенням для шаблону Run/While/Sleep.
додано Автор Alan, джерело
Я оновлю свою відповідь, щоб поговорити про вашу редагування. Я все ще стверджую, що підхід до запланованого завдання закінчується більш надійним рішенням для шаблону Run/While/Sleep.
додано Автор Alan, джерело
Я оновлю свою відповідь, щоб поговорити про вашу редагування. Я все ще стверджую, що підхід до запланованого завдання закінчується більш надійним рішенням для шаблону Run/While/Sleep.
додано Автор Alan, джерело
Ігнорувати назву статті Петра - це просто неправильно. Сон (), звичайно, може бути неправильно використаний, і часто це, але це не робить його внутрішньо неправильним або якийсь анти-структуру.
додано Автор Martin James, джерело
Ігнорувати назву статті Петра - це просто неправильно. Сон (), звичайно, може бути неправильно використаний, і часто це, але це не робить його внутрішньо неправильним або якийсь анти-структуру.
додано Автор Martin James, джерело
Я б не використовував поплавці, але подвійний у цьому випадку, оскільки неточність плаваючого вмісту може пошкодити вашу програму. (потік має лише близько 6 точних десяткових знаків після десяткової крапки)
додано Автор mmirwaldt, джерело
Thread.sleep набагато краще, ніж зайнятий чекає!
додано Автор mmirwaldt, джерело
Thread.sleep набагато краще, ніж зайнятий чекає!
додано Автор mmirwaldt, джерело
Thread.sleep набагато краще, ніж зайнятий чекає!
додано Автор mmirwaldt, джерело

8 Відповіді

Щоб вирішити цю проблему, вам доведеться переосмислити ваш дизайн. По суті, що ви робите, це робити роботу на регулярно запланованому інтервалі. Робота Run/While/Sleep працює, але, як показали ваші дослідження, це не оптимальне рішення.

Сучасні мови мають "задачу", що дозволяє середовищу програмування/ОС краще керувати виконанням ваших завдань.

У Java є java.util.timer разом з java.util.timertask . З шаблоном виконання завдань ви створюєте своє завдання і заплануєте його запустити через певний проміжок часу.

Таймери також надають вам більш чистий спосіб зупинки виконання циклу, скасовуючи таймер, замість встановлення логічного прапорця.

З коментарів:

Є кілька питань, про які треба знати. Якщо ваше завдання є тим, де завдання може працювати довше, ніж запланований інтервал, можна виконати інше завдання, коли попереднє завдання все ще виконується. Кілька рішень:

  1. Використовуйте чергу завдань, щоб чергувати роботу, яку потрібно виконати. Якщо в черзі немає роботи, завдання повертається.
  2. Інший підхід, що є загальним в JavaScript, полягає в тому, щоб запланувати завдання виконати один раз, і при завершенні завдання перевпорядкувати це завдання для виконання одноразового виконання.
  3. Виконує менш елегантний підхід, використовуючи прапорець, що вказує на конкретне завдання. Це працює, але вимагає правильного управління станом прапора, який схильний до помилок.

Інша поширені проблеми полягають у тому, що заплановані таймери часто застосовуються як найкращі зусилля. Тобто OS/Framework намагається запустити завдання заплановано, але не пропонує жодних гарантій виконання задачі точно на заданому інтервалі. Отже, якщо ваше завдання вимагає жорсткого, детермінованого планування, вам, імовірно, знадобиться більш детальне та апаратне рішення.

5
додано
@ csga5000: згоден. Є щось сказати, якщо це не зламане, не виправляйтеся. Тим не менш, з рішенням, що ґрунтується на вирішенні завдань, це дає змогу потенційно масштабувати розподілену модель.
додано Автор Alan, джерело
@mmirwaldt: Хороший момент. Я оновив свою відповідь, щоб розповісти про цю проблему, а також про те, що таймери не обов'язково детерміновані.
додано Автор Alan, джерело
@Alan дякую за це. Ти подарував мені функціональну альтернативу (мені подобається # 2), однак, я не бачу причини, чому я повинен змінити це, як на моєму поточному методі, використовуючи Thread.sleep ()
додано Автор csga5000, джерело
Дякую Алан за вашу розуміння. Я ніколи не любив таймерів. Я позначу вас як відповідь, але те, що сказано мерівардом, звучить як потенційна проблема, тому що я передбачаю можливість виконання завдання, зайнятого значним періодом часу, і моя програма може обробляти до тисячі "зразків", і тривалість, ніж заданий час, безумовно, становить занепокоєння. З тим, що у мене на даний момент це не викликає занепокоєння. Тож як я можу вирішити цю проблему?
додано Автор csga5000, джерело
Я відчуваю себе так, як я займався неточною System.sleep (), System.sleep() представляє для мене кращий варіант
додано Автор csga5000, джерело
Чи буде проблема з декількома потоками, які мають доступ до тих самих даних і працюють в той самий час ect. чи це буде щось інше mmirwaldt?
додано Автор csga5000, джерело
@ csga5000: Я додав відповідь на своє запитання зразком програми.
додано Автор mmirwaldt, джерело
Ви повинні переконатися, що ваше завдання завершено впродовж інтервалу (багато людей часто забувають, що згадати). Інакше ви отримаєте серйозні проблеми і повинні використовувати Thread.sleep замість змінних інтервалів.
додано Автор mmirwaldt, джерело

Щоб вирішити цю проблему, вам доведеться переосмислити ваш дизайн. По суті, що ви робите, це робити роботу на регулярно запланованому інтервалі. Робота Run/While/Sleep працює, але, як показали ваші дослідження, це не оптимальне рішення.

Сучасні мови мають "задачу", що дозволяє середовищу програмування/ОС краще керувати виконанням ваших завдань.

У Java є java.util.timer разом з java.util.timertask . З шаблоном виконання завдань ви створюєте своє завдання і заплануєте його запустити через певний проміжок часу.

Таймери також надають вам більш чистий спосіб зупинки виконання циклу, скасовуючи таймер, замість встановлення логічного прапорця.

З коментарів:

Є кілька питань, про які треба знати. Якщо ваше завдання є тим, де завдання може працювати довше, ніж запланований інтервал, можна виконати інше завдання, коли попереднє завдання все ще виконується. Кілька рішень:

  1. Використовуйте чергу завдань, щоб чергувати роботу, яку потрібно виконати. Якщо в черзі немає роботи, завдання повертається.
  2. Інший підхід, що є загальним в JavaScript, полягає в тому, щоб запланувати завдання виконати один раз, і при завершенні завдання перевпорядкувати це завдання для виконання одноразового виконання.
  3. Виконує менш елегантний підхід, використовуючи прапорець, що вказує на конкретне завдання. Це працює, але вимагає правильного управління станом прапора, який схильний до помилок.

Інша поширені проблеми полягають у тому, що заплановані таймери часто застосовуються як найкращі зусилля. Тобто OS/Framework намагається запустити завдання заплановано, але не пропонує жодних гарантій виконання задачі точно на заданому інтервалі. Отже, якщо ваше завдання вимагає жорсткого, детермінованого планування, вам, імовірно, знадобиться більш детальне та апаратне рішення.

5
додано
@ csga5000: згоден. Є щось сказати, якщо це не зламане, не виправляйтеся. Тим не менш, з рішенням, що ґрунтується на вирішенні завдань, це дає змогу потенційно масштабувати розподілену модель.
додано Автор Alan, джерело
@mmirwaldt: Хороший момент. Я оновив свою відповідь, щоб розповісти про цю проблему, а також про те, що таймери не обов'язково детерміновані.
додано Автор Alan, джерело
Дякую Алан за вашу розуміння. Я ніколи не любив таймерів. Я позначу вас як відповідь, але те, що сказано мерівардом, звучить як потенційна проблема, тому що я передбачаю можливість виконання завдання, зайнятого значним періодом часу, і моя програма може обробляти до тисячі "зразків", і тривалість, ніж заданий час, безумовно, становить занепокоєння. З тим, що у мене на даний момент це не викликає занепокоєння. Тож як я можу вирішити цю проблему?
додано Автор csga5000, джерело
@Alan дякую за це. Ти подарував мені функціональну альтернативу (мені подобається # 2), однак, я не бачу причини, чому я повинен змінити це, як на моєму поточному методі, використовуючи Thread.sleep ()
додано Автор csga5000, джерело
Я відчуваю себе так, як я займався неточною System.sleep (), System.sleep() представляє для мене кращий варіант
додано Автор csga5000, джерело
Чи буде проблема з декількома потоками, які мають доступ до тих самих даних і працюють в той самий час ect. чи це буде щось інше mmirwaldt?
додано Автор csga5000, джерело
@ csga5000: Я додав відповідь на своє запитання зразком програми.
додано Автор mmirwaldt, джерело
Ви повинні переконатися, що ваше завдання завершено впродовж інтервалу (багато людей часто забувають, що згадати). Інакше ви отримаєте серйозні проблеми і повинні використовувати Thread.sleep замість змінних інтервалів.
додано Автор mmirwaldt, джерело

Щоб вирішити цю проблему, вам доведеться переосмислити ваш дизайн. По суті, що ви робите, це робити роботу на регулярно запланованому інтервалі. Робота Run/While/Sleep працює, але, як показали ваші дослідження, це не оптимальне рішення.

Сучасні мови мають "задачу", що дозволяє середовищу програмування/ОС краще керувати виконанням ваших завдань.

У Java є java.util.timer разом з java.util.timertask . З шаблоном виконання завдань ви створюєте своє завдання і заплануєте його запустити через певний проміжок часу.

Таймери також надають вам більш чистий спосіб зупинки виконання циклу, скасовуючи таймер, замість встановлення логічного прапорця.

З коментарів:

Є кілька питань, про які треба знати. Якщо ваше завдання є тим, де завдання може працювати довше, ніж запланований інтервал, можна виконати інше завдання, коли попереднє завдання все ще виконується. Кілька рішень:

  1. Використовуйте чергу завдань, щоб чергувати роботу, яку потрібно виконати. Якщо в черзі немає роботи, завдання повертається.
  2. Інший підхід, що є загальним в JavaScript, полягає в тому, щоб запланувати завдання виконати один раз, і при завершенні завдання перевпорядкувати це завдання для виконання одноразового виконання.
  3. Виконує менш елегантний підхід, використовуючи прапорець, що вказує на конкретне завдання. Це працює, але вимагає правильного управління станом прапора, який схильний до помилок.

Інша поширені проблеми полягають у тому, що заплановані таймери часто застосовуються як найкращі зусилля. Тобто OS/Framework намагається запустити завдання заплановано, але не пропонує жодних гарантій виконання задачі точно на заданому інтервалі. Отже, якщо ваше завдання вимагає жорсткого, детермінованого планування, вам, імовірно, знадобиться більш детальне та апаратне рішення.

5
додано
@ csga5000: згоден. Є щось сказати, якщо це не зламане, не виправляйтеся. Тим не менш, з рішенням, що ґрунтується на вирішенні завдань, це дає змогу потенційно масштабувати розподілену модель.
додано Автор Alan, джерело
@mmirwaldt: Хороший момент. Я оновив свою відповідь, щоб розповісти про цю проблему, а також про те, що таймери не обов'язково детерміновані.
додано Автор Alan, джерело
@Alan дякую за це. Ти подарував мені функціональну альтернативу (мені подобається # 2), однак, я не бачу причини, чому я повинен змінити це, як на моєму поточному методі, використовуючи Thread.sleep ()
додано Автор csga5000, джерело
Я відчуваю себе так, як я займався неточною System.sleep (), System.sleep() представляє для мене кращий варіант
додано Автор csga5000, джерело
Чи буде проблема з декількома потоками, які мають доступ до тих самих даних і працюють в той самий час ect. чи це буде щось інше mmirwaldt?
додано Автор csga5000, джерело
Дякую Алан за вашу розуміння. Я ніколи не любив таймерів. Я позначу вас як відповідь, але те, що сказано мерівардом, звучить як потенційна проблема, тому що я передбачаю можливість виконання завдання, зайнятого значним періодом часу, і моя програма може обробляти до тисячі "зразків", і тривалість, ніж заданий час, безумовно, становить занепокоєння. З тим, що у мене на даний момент це не викликає занепокоєння. Тож як я можу вирішити цю проблему?
додано Автор csga5000, джерело
@ csga5000: Я додав відповідь на своє запитання зразком програми.
додано Автор mmirwaldt, джерело
Ви повинні переконатися, що ваше завдання завершено впродовж інтервалу (багато людей часто забувають, що згадати). Інакше ви отримаєте серйозні проблеми і повинні використовувати Thread.sleep замість змінних інтервалів.
додано Автор mmirwaldt, джерело

Ви повинні абсолютно уникати чистого спінінгу (другий ваш приклад) (за винятком окремих випадків використання, наприклад примірників синхронізації ), оскільки він споживає процесор для виконання не більше ніж чекати і може легко призвести до мертвих замків !

Саме тому ви часто використовуєте деяке затримку з спамом , щоб споживати менше процесора, і дає більше шансів на роботу інших потоків, зменшуючи ризик замків.

Отже, у вашому першому циклі немає нічого поганого, доки ви знаєте, що ви робите.

Але вам слід віддавати перевагу компонентам, присвяченим подібним випадкам використання: таймери .

1
додано

Замість sleep (), краще використовувати wait() і повідомити ().

При використанні sleep() потік виходить із черги, що запускається, і знову запускає потік, ОС має зробити додаткову роботу, яка є чистим накладним.

Але коли нитка чекає, вона не виходить з черги, яка працює, і тому ніяких накладних витрат.

0
додано

Оскільки Алан і Прагматек обидва сказали, таймери, як правило, віддають перевагу над використанням Thread.sleep (), однак, як зазначив Мартін Джеймс, "Sleep() може, безсумнівно, зловживати і часто, але це не робить його внутрішньо неправильним або якийсь анти-візерунка ".

Мій висновок полягає в тому, що, оскільки я використовую дельта, я виконує неточності Thread.sleep (). Це, як кажуть, немає проблем із його використанням. Таймер буде трохи складнішим для реалізації, тому що моє завдання доведеться закінчити, перш ніж зателефонувати знову таймеру, що з моєю програмою буде надзвичайно важко гарантувати.

Отже, в цій програмі та спосіб, яким я його використав, Thread.sleep() - це цілком реальний варіант.

0
додано
Таймери, безумовно, мають свої цілі, але занадто часто вони рекламуються як рішення, що відповідає всім вимогам, для всіх термінових подій. Спроба змусити таймери в отвори, які не підходять для них, часто призводить до непотрібного болю розробникам, очікуючи на синхронізацію виконання задачі таймера, перезаписування їх коду як стаціонарних машин тощо, замість простого виклику sleep ().
додано Автор Martin James, джерело

Оскільки Алан і Прагматек обидва сказали, таймери, як правило, віддають перевагу над використанням Thread.sleep (), однак, як зазначив Мартін Джеймс, "Sleep() може, безсумнівно, зловживати і часто, але це не робить його внутрішньо неправильним або якийсь анти-візерунка ".

Мій висновок полягає в тому, що, оскільки я використовую дельта, я виконує неточності Thread.sleep (). Це, як кажуть, немає проблем із його використанням. Таймер буде трохи складнішим для реалізації, тому що моє завдання доведеться закінчити, перш ніж зателефонувати знову таймеру, що з моєю програмою буде надзвичайно важко гарантувати.

Отже, в цій програмі та спосіб, яким я його використав, Thread.sleep() - це цілком реальний варіант.

0
додано
Таймери, безумовно, мають свої цілі, але занадто часто вони рекламуються як рішення, що відповідає всім вимогам, для всіх термінових подій. Спроба змусити таймери в отвори, які не підходять для них, часто призводить до непотрібного болю розробникам, очікуючи на синхронізацію виконання задачі таймера, перезаписування їх коду як стаціонарних машин тощо, замість простого виклику sleep ().
додано Автор Martin James, джерело

Як було поставлено питання про те, як таймер поводиться, якщо завдання таймера займає більше часу, ніж інтервал.

Я запрограмував дурну програму зразка, щоб подивитися, як поводиться таймер:

public class FlawedTimerTask extends TimerTask {
    final int taskId;
    final long sleepTime;
    int counter = 0;

    public FlawedTimerTask(int taskId, long sleepTime) {
        super();
        this.taskId = taskId;
        this.sleepTime = sleepTime;
    }

    @Override
    public void run() {
        long beginTimeInNs = System.nanoTime();
        System.out.println("taskId=" + taskId + ", run=" + counter + ", beginning at " + (beginTimeInNs-beginOfExperiment));
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTimeInNs = System.nanoTime();
        System.out.println("taskId=" + taskId + ", run=" + counter + ", ending at " + (endTimeInNs-beginOfExperiment));
        counter++;
    }

    static long beginOfExperiment;

    public static void main(String[] args) {
        beginOfExperiment = System.nanoTime();
        Timer timer = new Timer();
        timer.schedule(new FlawedTimerTask(1, 800), 500, 500);
        timer.schedule(new FlawedTimerTask(2, 1000), 500, 2500);
    }
}

Вихід був:

taskId=1, run=0, beginning at 491988762
taskId=1, run=0, ending at 1291944877
taskId=2, run=0, beginning at 1292056514
taskId=2, run=0, ending at 2293928680
taskId=1, run=1, beginning at 2294036467
taskId=1, run=1, ending at 3094967160
taskId=1, run=2, beginning at 3095097404
taskId=1, run=2, ending at 3894902745
taskId=1, run=3, beginning at 3895045820
taskId=1, run=3, ending at 4695902088
taskId=2, run=1, beginning at 4696095849
taskId=2, run=1, ending at 5695887973
taskId=1, run=4, beginning at 5695991911
taskId=1, run=4, ending at 6496896941
taskId=1, run=5, beginning at 6497002803
taskId=1, run=5, ending at 7297814161
taskId=1, run=6, beginning at 7297998297
taskId=1, run=6, ending at 8098803239
taskId=2, run=2, beginning at 8098922575
taskId=2, run=2, ending at 9098814787
taskId=1, run=7, beginning at 9098971977
taskId=1, run=7, ending at 9899803866
taskId=1, run=8, beginning at 9899970038
taskId=1, run=8, ending at 10699807458
taskId=1, run=9, beginning at 10699912038
taskId=1, run=9, ending at 11500693882
taskId=2, run=3, beginning at 11500815143
taskId=2, run=3, ending at 12501656270
taskId=1, run=10, beginning at 12501781380
taskId=1, run=10, ending at 13302714640
taskId=1, run=11, beginning at 13302888511
taskId=1, run=11, ending at 14102727215
taskId=1, run=12, beginning at 14102929958
taskId=1, run=12, ending at 14903695762
taskId=2, run=4, beginning at 14903878616
taskId=2, run=4, ending at 15903607223
taskId=1, run=13, beginning at 15903775961
taskId=1, run=13, ending at 16705705613
taskId=1, run=14, beginning at 16705798644
taskId=1, run=14, ending at 17505650180
taskId=1, run=15, beginning at 17505881795
taskId=1, run=15, ending at 18306578307
taskId=2, run=5, beginning at 18306718815
taskId=2, run=5, ending at 19306666847
taskId=1, run=16, beginning at 19306757953
taskId=1, run=16, ending at 20107480129
taskId=1, run=17, beginning at 20107580217
taskId=1, run=17, ending at 20907534407
taskId=1, run=18, beginning at 20907640911
taskId=1, run=18, ending at 21709616117
taskId=2, run=6, beginning at 21709784855
taskId=2, run=6, ending at 22709563506
taskId=1, run=19, beginning at 22709664236
taskId=1, run=19, ending at 23510559642
taskId=1, run=20, beginning at 23510653956
taskId=1, run=20, ending at 24310465713
taskId=1, run=21, beginning at 24310572217
taskId=1, run=21, ending at 25111451583
taskId=2, run=7, beginning at 25111549105
taskId=2, run=7, ending at 26111453508
taskId=1, run=22, beginning at 26111544614
taskId=1, run=22, ending at 26913489022
taskId=1, run=23, beginning at 26913629531
taskId=1, run=23, ending at 27713421398
taskId=1, run=24, beginning at 27713577305
taskId=1, run=24, ending at 28514443839
taskId=2, run=8, beginning at 28514550985
taskId=2, run=8, ending at 29514349525
taskId=1, run=25, beginning at 29514496450
taskId=1, run=25, ending at 30315367475
taskId=1, run=26, beginning at 30315469488
taskId=1, run=26, ending at 31115349896
taskId=1, run=27, beginning at 31115475648
taskId=1, run=27, ending at 31917465609
taskId=2, run=9, beginning at 31917563773
taskId=2, run=9, ending at 32917368087
taskId=1, run=28, beginning at 32917524636
taskId=1, run=28, ending at 33718337276
taskId=1, run=29, beginning at 33718481634
taskId=1, run=29, ending at 34518366533
taskId=1, run=30, beginning at 34518459564
taskId=1, run=30, ending at 35319336363
taskId=2, run=10, beginning at 35319516009
taskId=2, run=10, ending at 36319338930
taskId=1, run=31, beginning at 36319440301
taskId=1, run=31, ending at 37121299378
taskId=1, run=32, beginning at 37121403957
taskId=1, run=32, ending at 37921223413
taskId=1, run=33, beginning at 37921324785
taskId=1, run=33, ending at 38722168863
taskId=2, run=11, beginning at 38722270877
taskId=2, run=11, ending at 39722259328

Ви можете це зауважити

  • таймер працює в інтервалі завдань таймера (800 мс, 1000 мс), а не в інтервалі 500 мс
  • не відбувається перемішування між завданнями таймера (в основному циклі є блокування викликів при реалізації таймера, який є однопоточеним)
0
додано
ІТ КПІ - Java
ІТ КПІ - Java
436 учасників