Форум программистов, компьютерный форум, киберфорум
Наши страницы
Программирование Android
Войти
Регистрация
Восстановить пароль
 
Excalibur225
0 / 0 / 0
Регистрация: 24.12.2017
Сообщений: 18
1

Синхронизация вычислений рабочего потока и отрисовки в UI потоке

30.01.2018, 21:32. Просмотров 350. Ответов 10
Метки нет (Все метки)

В общем в Ui потоке должна идти пошаговая отрисовка ячеек, которые лежат в GridView, а в рабочем созданном потоке производятся вычисления ячеек и соответственно после каждой итерации вычислений должна происходить отрисовка.
Я реализовал это через post, но в процесса работы все таки отрисовываются не совсем корректно. Такое ощущение как будто поток интерфейса не успевает обработать сообщение и перерисовать элементы, а рабочий поток уже переходит на другую итерацию вычислений, хотя насколько я понимаю, все должно работать синхронно.
Вот пример как я сделал:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
final Timer executor = new Timer();
        executor.schedule(new TimerTask() {
            @Override
            public void run() {
                if(isBeingDrawn == false){
                    
                    //---тут вычисления
 
                    //---флаг говорит что идет отрисовка
                    isBeingDrawn = true;
 
                    //---собственно синхронизирую
 
                    gridView.post(new Runnable() {
                        @Override
                        public void run() {
 
                            //---тут он перерисовывает в UI потоке, насколько я понял
                            adapter.notifyDataSetChanged();
 
                            //---и далее говорит что отрисовка закончилась
                           //---чтобы продолжить вычисления 
                            isBeingDrawn = false;
                        }
                    });
 
                }
            }
        }, 0, 20);
    }
И вот такой вопрос: правильно я понимаю что в UI потоке при обработке сообщения из рабочего потока сначала адаптер уведомляет о изменении данных и запрашивает перерисовать, а после флагу присваивается значение false, чтобы рабочий поток продолжил вычисления? Или у меня есть ошибки? Заранее спасибо за ответ
П.С. первый раз спрашиваю на форму, так что извиняйте, если неправильно оформил))
0
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
30.01.2018, 21:32
Ответы с готовыми решениями:

Как получить значение String из UI потока в потоке AsyncTask?
Есть 4 шт. EditText. Нужно получить их значения в потоке AsyncTask. Вот весь...

Как из одного потока вызвать метод таким образом, чтобы он выполнялся в другом потоке
Значит имеется некая активность, которая запускает сервер по нажатии кнопки,...

Создать дочернее окно в новом потоке для отрисовки рисунка
Как создать дочернее окно в новом потоке?мне в дочернем окне надо рисовать. Я...

Создание отдельного потока для отрисовки змейки
partial class View : UserControl { Model model; public...

Выполнение длительных вычислений и операций в отдельном потоке
Добрый вечер. Возникла проблема с вычислениями. Из базы данных (файл)...

10
VASSUV
MiThEoN
443 / 307 / 35
Регистрация: 31.10.2009
Сообщений: 505
Записей в блоге: 2
Завершенные тесты: 1
31.01.2018, 05:30 2
Попробуйте так:
Java
1
2
3
4
5
6
7
8
9
10
11
new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message inputMessage) {
         //---тут он перерисовывает в UI потоке, насколько я понял
         gridview.getAdapter().notifyDataSetChanged();
 
         //---и далее говорит что отрисовка закончилась
         //---чтобы продолжить вычисления
         isBeingDrawn = false;
     }
};
1
Excalibur225
0 / 0 / 0
Регистрация: 24.12.2017
Сообщений: 18
01.02.2018, 00:01  [ТС] 3
Цитата Сообщение от VASSUV Посмотреть сообщение
Попробуйте так:
JavaВыделить код
1
2
3
4
5
6
7
8
9
10
11
new Handler(Looper.getMainLooper()) {
* * @Override
* * public void handleMessage(Message inputMessage) {
* * * * *//---тут он перерисовывает в UI потоке, насколько я понял
* * * * *gridview.getAdapter().notifyDataSetChanged();
//---и далее говорит что отрисовка закончилась
* * * * *//---чтобы продолжить вычисления
* * * * *isBeingDrawn = false;
* * *}
};
К сожалению таким способом он просто отказывается перерисовывать картинку и виснет при уведомлении адаптера
0
VASSUV
MiThEoN
443 / 307 / 35
Регистрация: 31.10.2009
Сообщений: 505
Записей в блоге: 2
Завершенные тесты: 1
01.02.2018, 00:03 4
Зачем вам так часто нужно обновление? Делайте обновление допустим раз в 1 секунду
0
Excalibur225
0 / 0 / 0
Регистрация: 24.12.2017
Сообщений: 18
01.02.2018, 01:32  [ТС] 5
Цитата Сообщение от VASSUV Посмотреть сообщение
Зачем вам так часто нужно обновление? Делайте обновление допустим раз в 1 секунду
Да, это конечно поможет, но смысл как раз в том, чтобы была синхронизация. В любом случае вопрос можно считать разрешенным, тк как оказалось все работает синхронно, а не очень корректное отображение скорее всего из-за плохо оптимизированного адаптера. Нужно будет еще что-то придумывать для улучшения оптимизации В любом случае, спасибо за ответы
0
VASSUV
MiThEoN
443 / 307 / 35
Регистрация: 31.10.2009
Сообщений: 505
Записей в блоге: 2
Завершенные тесты: 1
01.02.2018, 13:28 6
Вы создаете handler или runnable чаще чем они успевают выполнится. Потому что ваши вычисления легче чем запуск этих вставок на ui поток.
Если бы Ваши вычисления выполнялись допустим больше секунды, не было бы и проблем

Добавлено через 7 часов 1 минуту
Java
1
2
                    //---флаг говорит что идет отрисовка
                    isBeingDrawn = true;
Вставте сюда Thread.sleep()

Java
1
2
3
                    //---флаг говорит что идет отрисовка
                    Thread.sleep(3000);
                    isBeingDrawn = true;
И постмотрите что изменится
0
Excalibur225
0 / 0 / 0
Регистрация: 24.12.2017
Сообщений: 18
03.02.2018, 22:28  [ТС] 7
Цитата Сообщение от VASSUV Посмотреть сообщение
Вы создаете handler или runnable чаще чем они успевают выполнится. Потому что ваши вычисления легче чем запуск этих вставок на ui поток.
Если бы Ваши вычисления выполнялись допустим больше секунды, не было бы и проблем
Да на самом деле при паузе в 200 миллисек уже работает корректно, но все же я не совсем вас понял. У меня не может создаваться еще один Runnable, если первый еще не завершен(по крайней мере действия в нём).

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
executor.schedule(new TimerTask() {
            @Override
            public void run() {
                if(isBeingDrawn == false){
                    
                    //---тут вычисления
 
                    //---флаг говорит что идет отрисовка
                    isBeingDrawn = true;
 
                    //---собственно синхронизирую
 
                    gridView.post(new Runnable() {
                        @Override
                        public void run() {
 
                            //---тут он перерисовывает в UI потоке, насколько я понял
                            adapter.notifyDataSetChanged();
 
                            //---и далее говорит что отрисовка закончилась
                           //---чтобы продолжить вычисления 
                            isBeingDrawn = false;
                        }
                    });
 
                }
            }
        }, 0, 20);
Рабочий поток по сути ничего не будет делать, тк не сможет зайти в секцию после if если флаг еще не возвращен в состояние false из UI потока, который будет возвращен только после adapter.notifyDataSetChanged(), те перерисовки экрана. Ну и соответственно это гарантирует ситуацию, когда новое сообщение для перерисовки будет отправляться только после успешного завершения текущей и установки флага в false.
0
VASSUV
MiThEoN
443 / 307 / 35
Регистрация: 31.10.2009
Сообщений: 505
Записей в блоге: 2
Завершенные тесты: 1
03.02.2018, 23:33 8
Новый runnable это как новая инъекция своего кода в ui поток.
А теперь представьте что вы делаете эти инъекции постоянно в цикле без какой либо задержки
Сама инъекция уже требует времени из ui потока даже если ни какого кода выполнятся не будет
И получается что вы забивает ui поток этими инъекциями.
Поток забивают не ваши вычисления, и даже не отрисовка. А именно эти инъекции

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

Никогда не запускайте runnable в цикле. Возьмите себе за правило такой подход.

Какого рода вычисления у вас? И какие данные переходят из потока в поток?
Может придумаем для вас решение получше

Добавлено через 2 минуты
Можете убедиться в моих словах путем логировани. И по времени посмотрите что где и сколько раз запускается
0
Excalibur225
0 / 0 / 0
Регистрация: 24.12.2017
Сообщений: 18
04.02.2018, 22:24  [ТС] 9
Цитата Сообщение от VASSUV Посмотреть сообщение
Какого рода вычисления у вас? И какие данные переходят из потока в поток?
Может придумаем для вас решение получше
game of life клеточная игра) Ну по сути при запуске в параллельный поток передается два массива объектов с полем boolean, а также еще некоторые переменные, точнее все это находится в классе логики, и уже в методе запускается этот поток для расчетов, используя данные поля и отправляя эти самые инъекции в графический поток для отрисовки. Соответственно делать итерации изменений поля в анимации имеет смысл когда fps ну хотя бы 20-30, те хотябы с задержкой не более чем 50мс. Сами вычисления легкие по сути. И вот я тогда даже не знаю каким образом можно реализовать, если вариант с циклом так загружает главный поток
Вот код метода с параллельным расчетом:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
void startGame(final GridView gameField,
                   final CellsAdapter adapter,
                   final ToggleButton gameRunningSwitch,
                   final Button oneStepButton)
    {
        isGameRunning = true;
        oneStepButton.setEnabled(false);
        final Timer gameExecutionTimer = new Timer();
        gameExecutionTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                if(!isBeingDrawn){
                    //------------------make a calculation part------------
                    if(unlimitedBorders){
                        makeOneStepUnlimitedBorders();
                    }
                    else{
                        makeOneStep();
                    }
                    //-------------------redraw in main thread-------------
                    isBeingDrawn = true;
                    gameField.post(new Runnable() {
                        @Override
                        public void run() {
                            adapter.notifyDataSetChanged();
                            isBeingDrawn = false;
                        }
                    });
                    //-------------------------checking condition------------------
                    adjGenerationsHaveDifference = false;
                    for (int i = 0; i < rowAmount; i++) {
                        for (int j = 0; j < columnAmount; j++) {
                            if (currentGeneration[i][j].isAlive() != gameFieldData[i][j].isAlive()) {
                                adjGenerationsHaveDifference = true;
 
                            }
                        }
                    }
                    if(!isGameRunning){
                        oneStepButton.post(new Runnable() {
                            @Override
                            public void run() {
                                oneStepButton.setEnabled(true);
                            }
                        });
                        gameExecutionTimer.cancel();
                        gameExecutionTimer.purge();
                        return;
                    }
                    if ((!adjGenerationsHaveDifference) || (aliveCellsAmount == 0)) {
                        gameRunningSwitch.post(new Runnable() {
                            @Override
                            public void run() {
                                gameRunningSwitch.setChecked(false);
                            }
                        });
                        oneStepButton.post(new Runnable() {
                            @Override
                            public void run() {
                                oneStepButton.setEnabled(true);
                            }
                        });
                        isGameRunning = false;
                        gameExecutionTimer.cancel();
                        gameExecutionTimer.purge();
                    }
                }
            }
        }, 0, 50);
    }
Хотя конечно если увеличить количество строк и столбцов уже примерно до 30, то уже начинает явно тормозить даже без непрерывной анимации.
0
VASSUV
MiThEoN
443 / 307 / 35
Регистрация: 31.10.2009
Сообщений: 505
Записей в блоге: 2
Завершенные тесты: 1
05.02.2018, 09:47 10
Мое мнение такое. Поставь себе либу RxJava. Хорошо что есть по ней много туториалов, без труда сможешь вникнуться.

Как подключишь, придется написать что то вроде этого, не более
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        AtomicInteger nextValue = new AtomicInteger(1);
        Disposable disposable = Observable.<AtomicInteger>create(o -> {
            while(!o.isDisposed()) {
                nextValue.incrementAndGet();
                /** Здесь сделать вычисления, Желательно вынести в отдельный метод **/
                o.onNext(nextValue);
            }
            o.onComplete();
        })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        v -> refreshUi(v),
                        e -> e.printStackTrace(),
                        () -> System.out.println("Completed")
                );
Java
1
2
3
    private void refreshUi(AtomicInteger v) {
        /**     Как то обновляешь UI    **/ 
    }
Java
1
2
3
        /**    Когда нужно будет завершить  
               Стоит вызвать dispose()        **/
        disposable.dispose();
Если будут еще фризы, то поставить после onNext(***) задержку в 30мс

Да и, у тебя есть массивы данных, которые как я понял обновляются в другом потоке, и выводятся на UI
Если они хоть как то будут обновляться на UI, их сразу следует сделать другим типом AtomicArray(точно не помню), чтобы не было коллизий данных между потоками
1
Excalibur225
0 / 0 / 0
Регистрация: 24.12.2017
Сообщений: 18
06.02.2018, 18:53  [ТС] 11
Спасибо, буду читать и разбираться
Насчет обновления массивов. В том месте, где используется параллельный поток, они обновляются только в нём во избежание как раз конфликтов между потоками.
0
06.02.2018, 18:53
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
06.02.2018, 18:53

Показывать ProgressBar вычислений, запущенных в отдельном потоке
На главной форме есть кнопка при нажатии создается новый поток: private void...

Закрыть форму во втором потоке при окончании вычислений в первом
Утро доброе. есть главная форма, в ней выполняются долгие вычисления...

Синхронизация асинхронных событий в одном потоке
Добрый день! Работаю с веб сервисами. Столкнулся со следующей проблемой на...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru