Russian Qt Forum
Ноябрь 23, 2024, 08:36 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3 4   Вниз
  Печать  
Автор Тема: Многопоточное осветление пикселей  (Прочитано 25538 раз)
AlphaGh0St
Гость
« Ответ #15 : Ноябрь 24, 2011, 22:56 »

Идея с контейнерами хороша. Но видите ли, задача данной программы: продемонстрировать преимущества многопоточности.
Программа рассчитана на слабую аудиторию, которая и в программировании-то не особо сильна, а Qt и в глаза не видела.

По этому хотелось бы сделать код на столько простым, насколько это вообще возможно.

Igors, спасибо за помощь! Я учту Ваши советы и продолжу работать над программой, однако, считаю, что контейнеры включать не стоит, т.к. из аудитории с STL врятли кто знаком.

В данном случае, главным является простота кода, пусть даже в ущерб эффективности.
Записан
Sahab
Гость
« Ответ #16 : Ноябрь 24, 2011, 23:06 »

В данной задаче OpenMP ни к селу ни к городу. Ну разве что есть желание "пособирать"  Улыбающийся
ну смотря как на это смотреть...
учитывая
Цитировать
Программа, демонстрирующая преимущества многопоточности в Qt.
- то не подойдет.
если осветление пикселей в отдельном потоке - тоже.
если
Цитировать
Многопоточное осветление пикселей
то вполне подойдет.
хотя все это оффтоп
Записан
AlphaGh0St
Гость
« Ответ #17 : Ноябрь 24, 2011, 23:44 »

Igors, Я изменил метод run() в соответствии с Вашими рекомендациями, но проблема осталась.

Пытаясь локализовать ошибку, я выводил отладочную информацию в каждом потоке, она состоит из сообщения о том, что поток с определённым ID начал работу, общее кол-во потоков, номер выполняемого потока, и сообщение о том, что поток завершился.

Вот отладочная информация при 1-м потоке:
Код:
thread begin # 0xf60 
team count   : 1
self index   : 0
thread done # 0xf60

При 2-х потоках:
Код:
thread begin # 0xc2c 
thread begin # 0xa14
team count   : 2
team count   : 2
self index   : 0
 
self index   : 1
 
thread done # 0xc2c

При 3-х потоках:
Код:
thread begin # 0x208 
thread begin # 0xe18
team count   : 3
thread begin # 0xf84
team count   : 3
self index   : 0
 
team count   : 3
self index   : 1
 
self index   : 2
 
thread done # 0x208

При 4-х потоках:
Код:
thread begin # 0x5fc 
thread begin # 0x8d0
team count   : 4
team count   : 4
thread begin # 0x9cc
self index   : 0
 
self index   : 1
 
thread begin # 0x4f8
team count   : 4
team count   : 4
self index   : 2
 
self index   : 3
 
thread done # 0x5fc

Завершается первый созданный поток и завершается вся программа.
Тут явно что-то не так... Но я никак не могу понять, что именно...
« Последнее редактирование: Ноябрь 24, 2011, 23:46 от AlphaGh0St » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #18 : Ноябрь 25, 2011, 10:43 »

Примерный код главной нитки
Код
C++ (Qt)
#define NUM_THREAD  4     // просто чтобы попроще
 
// здесь загружаем исходный t_image
 
// объявляем массив ниток
ImageThread theThread[NUM_THREAD];
 
// стартуем массив ниток
for (int i = 0; i < NUM_THREAD; ++i) {
theThread[i].mSelfIndex = i;              
theThread[i].mTeamCount = NUM_THREAD;
theThread[i].start();
}
 
// ждем пока все сделано
for (int i = 0; i < NUM_THREAD; ++i)
theThread[i].wait();
 
// здесь выводим "осветленный" t_image
 
Минус "просто массива" в том что нельзя передать параметры в конструктор каждого элемента, ну это можно перетерпеть
Записан
AlphaGh0St
Гость
« Ответ #19 : Ноябрь 27, 2011, 12:28 »

Фуууух, наконец-то нашёл, в чём проблема.
Первый поток работает с указателем t_image, который указывает на QImage, содержащий картинку.
Когда начинает работать второй поток, указатель t_image почему-то содержит уже другой адрес, а потому программа и вылетала.

Сделал указатель t_image статическим, проблема решилась.

Igors, большое спасибо за помощь!
Записан
AlphaGh0St
Гость
« Ответ #20 : Ноябрь 28, 2011, 19:20 »

Прям какое-то колдовство!
Работала программа, всё замечательно, и тут СНОВА та же проблема.

При запуске программы в нескольких потоках, она вылетает.
Отладчик показал, что программа вылетает из-за ошибки сегментации (SIGSEGV).
И вылетает на этой строке:
Код:
color.setRgb(qRed(pix), qGreen(pix), qBlue(pix));

Но гораздо чаще, вот на этой:
Код:
pix = qRgba(color.red(), color.green(), color.blue(), qAlpha(pix));

Сколько уже с ней провозился, никак не могу понять, в чём тут дело...
Если кто знает, подскажите, пожалуйста.

Ниже привожу код:
Код:
void ImageThread::run()
{
    QColor color;
    int h = 0, s = 0, v = 0;

    time.start(); // замеряем время выполнения

    int numRow = t_image->height() / t_TeamCount;  // число строк для обработки
    int beginRow = numRow * t_SelfIndex; // первая стока
    int endRow = (t_SelfIndex == t_TeamCount - 1) ? t_image->height() : (beginRow + numRow); // предел

    for(int row = beginRow; row < endRow; ++row){
        t_mutexRead.lock();
            unsigned int *data = (unsigned int *) t_image->scanLine(row);
        t_mutexRead.unlock();

        for(int column = 0; column < t_image->width(); ++column){
            t_mutexRead.lock();
                unsigned int &pix = data[column];
            t_mutexRead.unlock();

            color.setRgb(qRed(pix), qGreen(pix), qBlue(pix));
            color.getHsv(&h, &s, &v);
            v += t_brightnessValue; // значение, на которое надо увеличить яркость
            v = qMin(v, 255);
            color.setHsv(h, s, v);

            // сохраняем изменённый пиксель
            t_mutexWrite.lock();
                pix = qRgba(color.red(), color.green(), color.blue(), qAlpha(pix));
            t_mutexWrite.unlock();
         }
     }

    // сообщаем главному классу, что работа завершена и время выполнения.
    emit workDone("Ms: " + QString("%1").arg(time.elapsed()));
}

Запускаем потоки:
Код:
    // кол-во потоков
    int threadsLimit = ui->threadsSpinBox->text().toInt();

    // устанавливаем значения для потоков (это всё статические данные)
    imgThread[0].setBrightnessValue(ui->valueSpinBox->text().toInt()); // уровень яркости
    imgThread[0].setImagePointer(&image); // указатель на картинку
    imgThread[0].setTeamCount(threadsLimit); // всего потоков

    // запускаем потоки
    for(int i = 0; i < threadsLimit; i++){
        imgThread[i].setSelfIndex(i);
        imgThread[i].start();
        }
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Ноябрь 28, 2011, 20:24 »

1) Надо верить "самому себе", (ну или "уважать себя" - смысл тот же). Мы сказали/утверждаем что каждая нитка работает со своей частью имеджа, таким образом нитки не пересекаются. Значит нехрен боязливо вставлять мутексы "на всякий случай" - тем более что вылетов они все равно никак не ловят.

2) По каким-то сторонним причинам мы не хотим делать нормальный контейнер указателей - но мы запрашиваем число ниток у пользователя. Ладно, так надо - значит надо, Вам виднее. Но тогда надо как минимум проверять не превысит ли введенное пользователем число ниток размер массива. Плюс называть вещи своими именами, а не так себе 
Код
C++ (Qt)
 // устанавливаем значения для потоков (это всё статические данные)
   imgThread[0].setBrightnessValue(ui->valueSpinBox->text().toInt()); // уровень яркости
   imgThread[0].setImagePointer(&image); // указатель на картинку
   imgThread[0].setTeamCount(threadsLimit); // всего потоков
 
Откуда видно что это static? Никак не видно. давайте так
Код
C++ (Qt)
   ImageThread::setBrightnessValue(ui->valueSpinBox->text().toInt());
   ImageThread::setImagePointer(&image);
   ImageThread::setTeamCount(threadsLimit);
 
Вот это static и это ясно без комментариев

3) Вылетает - нормально, у меня тоже ни одна multi-threaded задача сходу не пошла (на то она и  multi-threaded). Спокойно вставляем отладочную печать в run нитки, напр
Код
C++ (Qt)
printf("thread %p. image = %p (%d x %d) t_TeamCount = %d, t_SelfIndex = %d. begRow = %d, endRow = %d\n".
// здесь переменные
 

Др. словами - надо почиститься, навести порядок - и все получится (куда она денется)
Записан
AlphaGh0St
Гость
« Ответ #22 : Ноябрь 29, 2011, 00:09 »

1) Когда я только начинал изучать потоки, вычитал, что доступ к совместно используемым данным, нужно блокировать мьютексом.
Ну вот это правило себе в голову сразу и вбил.
Собственно, да, в этом случае мьютексы и не нужны, можно их убрать.

2) Да, в программе задаётся массив ImageThread imgThread[4]; , а кол-во нужных потоков, пользователь выбирает сам. Пользователь не сможет задать более 4-х потоков, спасибо QSpinBox'у, который задаёт ограничение.

К слову о статических данных, тут Вы абсолютно правы, я недоглядел.

3) В каждой итерации второго (вложенного) цикла, вывожу отладочную информацию, такой строкой:
Код:
qDebug() << "#" << this->currentThreadId()
         << " img:" << &t_image
         << " team_count:" << t_TeamCount
         << " self_index:" << t_SelfIndex
         << " row:" << row
         << " col:" << column
         << " begRow:" << beginRow
         << " endRow:" << endRow;


Получаем следующий вывод. Первый запуск (2 потока). (в связи с большим кол-вом информации, часть заменена на "..."):
Код:
# 0x928  img: 0x41602c  team_count: 2  self_index: 0  row: 0  col: 0  begRow: 0  endRow: 97 
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 0  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 1  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 2  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 3  begRow: 97  endRow: 194
...
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 154  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 155  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 156  begRow: 97  endRow: 194
...
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 100  col: 15  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 100  col: 16  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 100  col: 17  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 100  col: 18  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 100  col: 19  begRow: 97  endRow: 194
Программа неожиданно завершилась.


Второй запуск, 2 потока, другое изображение:
Код:
# 0x220  img: 0x41602c  team_count: 2  self_index: 0  row: 0  col: 0  begRow: 0  endRow: 302 
# 0x220  img: 0x41602c  team_count: 2  self_index: 0  row: 0  col: 1  begRow: 0  endRow: 302
# 0x220  img: 0x41602c  team_count: 2  self_index: 0  row: 0  col: 2  begRow: 0  endRow: 302
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 0  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 1  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 2  begRow: 302  endRow: 604
...
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 345  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 346  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 347  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 348  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 349  begRow: 302  endRow: 604
...
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 628  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 629  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 630  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 631  begRow: 302  endRow: 604
# 0xb74  img: 0x41602c  team_count: 2  self_index: 1  row: 302  col: 632  begRow: 302  endRow: 604
Программа неожиданно завершилась.


Дебаггер так же сообщает об ошибке сегментации.
Останавливается на строке
Код:
color.setRgb(qRed(pix), qGreen(pix), qBlue(pix));
И ещё пишет о *data <unavailable synchronous data>
и о &pix <not accessible>.

В отладочной информации, я не заметил ничего необычного, хотя...может плохо смотрел.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #23 : Ноябрь 29, 2011, 10:39 »

1) Когда я только начинал изучать потоки, вычитал, что доступ к совместно используемым данным, нужно блокировать мьютексом.
Все Вы правильно вычитали, но у Вас "идеальный случай" когда можно (и нужно) обойтись без блокировок

3) В каждой итерации второго (вложенного) цикла, вывожу отладочную информацию, такой строкой:

Получаем следующий вывод. Первый запуск (2 потока). (в связи с большим кол-вом информации, часть заменена на "..."):
Код:
# 0x928  img: 0x41602c  team_count: 2  self_index: 0  row: 0  col: 0  begRow: 0  endRow: 97 
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 0  begRow: 97  endRow: 194
# 0x21c  img: 0x41602c  team_count: 2  self_index: 1  row: 97  col: 1  begRow: 97  endRow: 194
...
Оба-на - ведь должна быть одна печать на 1 нитку, и все. Почему вторая печатается много раз? Баг, разбирайтесь. И что значит "второго" - непонятно где тут "вложенный", поясните
Записан
AlphaGh0St
Гость
« Ответ #24 : Ноябрь 29, 2011, 11:01 »

Ну как же?
Код:
  for(int row = beginRow; row < endRow; ++row){ // первый цикл
        ...
        for(int column = 0; column < t_image->width(); ++column){ // второй (вложенный)
            ...
            // вот ТУТ во втором цикле и вывожу отладочную информацию
            }
        }
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #25 : Ноябрь 29, 2011, 11:29 »

Нет, перенесите печать выше, сначала надо убедиться что все нитки "получили свои куски". Плюс последнюю строку (выдача сигнала) для отладки лучше закомментировать

Если случай "совсем тяжелый" и/или "надо срочно" - скиньте исходники, можно в личку
Записан
AlphaGh0St
Гость
« Ответ #26 : Ноябрь 29, 2011, 12:25 »

Перенёс вывод отладочной информации в первый цикл, получил следующее:
Код:
# 0xfc8  img: 0x41602c  team_count: 2  self_index: 1  row: 302  begRow: 302  endRow: 604 
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 0  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 1  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 2  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 3  begRow: 0  endRow: 302
...
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 129  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 130  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 131  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 132  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 133  begRow: 0  endRow: 302
...
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 297  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 298  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 299  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 300  begRow: 0  endRow: 302
# 0xcc0  img: 0x41602c  team_count: 2  self_index: 0  row: 301  begRow: 0  endRow: 302
Программа неожиданно завершилась.

Случай не то, чтобы совсем уж тяжёлый, но проблематичный.
Исходники прямо сюда скину, у меня секретов нет.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #27 : Ноябрь 29, 2011, 13:59 »

Собрал, посмотрел, все "как доктор прописал" (Picture 1) - только вот чего Вы wait закомментарили (Picture 2)? Так нельзя, главная нитка обязательно должна дождаться пока все рабочие завершатся. Оно конечно имя "wait" неинтуитивное (в оригинале pthread_join) но тем не менее.

К слову: не пишите русских комментариев, как правильно советует наш модератор (Picture 2). Рано или поздно "plain English" придется освоить, так что затягивать незачем.

Если вылет на др. имедже - загрузите, т.к. пока нечего искать
« Последнее редактирование: Ноябрь 29, 2011, 14:01 от Igors » Записан
AlphaGh0St
Гость
« Ответ #28 : Ноябрь 29, 2011, 17:24 »

чего Вы wait закомментарили?
Думал, что wait тут не нужен. Понятное дело, что в случае консольной программы, главный поток должен ждать завершения дочерних. Но вот не думал, что это нужно делать и в программах с GUI, т.к. главный поток не завершится после запуска дочерних.

Цитировать
не пишите русских комментариев. Рано или поздно "plain English" придется освоить
Да с Английским у меня проблем особых нет. В данном случае комментарии на Русском т.к. программа предназначена для слабой аудитории, которая не сильна ни в программировании, ни в Английском. По этому решил, что комментарии на Русском будут проще для восприятия.

Цитировать
Если вылет на др. имедже - загрузите, т.к. пока нечего искать
...Что-то не уловил мысль...
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #29 : Ноябрь 29, 2011, 19:28 »

Если вылет на др. имедже - загрузите, т.к. пока нечего искать
...Что-то не уловил мысль...
Ну просто пока Ваше приложение работает и не валится  Улыбающийся Может вылетает на др исходных данных (имедже) так покажите на каком.

Примечание: менять "saturation" тоже очень полезно/наглядно, даже чаще нужно чем "value". Я бы добавил опцийку (совсем недавно писал такой шейдер). Ну и слайдеры бы неплохо смотрелись (впрочем дело вкуса)
Записан
Страниц: 1 [2] 3 4   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.189 секунд. Запросов: 23.