Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: qt_user от Ноябрь 20, 2011, 00:31



Название: Приостановить поток
Отправлено: qt_user от Ноябрь 20, 2011, 00:31
Доброй ночи,
1. В общем дело таково: мне нужно приостановить выполнение потока, в принципе для этого есть условие ожидания
QWaitCondition::wait(), но эта ф-ция принимает мютекс, мне же просто хочется приостановить поток, мютексом
в холостую lock'aть unlock'ать как-то "по-быдлокодерски", есть какие-то варианты?

2. и еще одно: если у меня есть перечисление и переменная:
Код:
enum WorkState {run, stop, exit};
WorkState workState

эту переменную workState разделяют 2 потока:
workState = run;
if (workState == stop) {}
Вопрос: это атомарные операции?

Спасибо


Название: Re: Приостановить поток
Отправлено: Igors от Ноябрь 20, 2011, 10:36
1) В любом случае требуется мутекс, семафор, на худой конец - атомарный лок

2) Эти операции атомарные, но чтение+запись такой переменной нужно защищать блокировками


Название: Re: Приостановить поток
Отправлено: alexman от Ноябрь 20, 2011, 11:49
1. void QThread::sleep ( unsigned long secs )


Название: Re: Приостановить поток
Отправлено: qt_user от Ноябрь 20, 2011, 11:50
1) В любом случае требуется мутекс, семафор, на худой конец - атомарный лок
а что такое атомарный лок? и еще такой вопрос - с точки зрения произовдительности что лучше:
lock() unlock() мютексом или release() aquire() семафором? в данном семафор всеравно будет использоваться
как мютекс


Название: Re: Приостановить поток
Отправлено: qt_user от Ноябрь 20, 2011, 11:51
1. void QThread::sleep ( unsigned long secs )
это не пойдет, неизвестно сколько потоку нужно спать, точнее будет его определенное событие (нажатие на кнопку)


Название: Re: Приостановить поток
Отправлено: alexman от Ноябрь 20, 2011, 12:35
Ну можно уснуть на немного и проверить состояние, если состояние не выполнено, то можно снова уснуть на немного, ... пока не получим необходимое состояние.


Название: Re: Приостановить поток
Отправлено: BRE от Ноябрь 20, 2011, 12:56
1. В общем дело таково: мне нужно приостановить выполнение потока, в принципе для этого есть условие ожидания
QWaitCondition::wait(), но эта ф-ция принимает мютекс, мне же просто хочется приостановить поток, мютексом
в холостую lock'aть unlock'ать как-то "по-быдлокодерски", есть какие-то варианты?
А у тебя не получится в холостую локать/унлокать. :)
Тебе все равно потребуется какой-то "ресурс", что бы указать засыпать потоку или нет.


Название: Re: Приостановить поток
Отправлено: BRE от Ноябрь 20, 2011, 13:07
Ну можно уснуть на немного и проверить состояние, если состояние не выполнено, то можно снова уснуть на немного, ... пока не получим необходимое состояние.
sleep "плохая" функция. :)
IMHO, почти всегда (если не всегда) можно обойтись без нее и получить более эффективный код.


Название: Re: Приостановить поток
Отправлено: Igors от Ноябрь 20, 2011, 13:09
а что такое атомарный лок? и еще такой вопрос - с точки зрения произовдительности что лучше:
lock() unlock() мютексом или release() aquire() семафором? в данном семафор всеравно будет использоваться
как мютекс
Производительность мутекса и семафора одинакова (они сделаны на одних и тех же базовых примитивах ОС). Ну и скоростью оба не блещут. Поэтому если время останова достаточно мало (0.1 сек и меньше) то лучше атомарный лок, который не останавливает нитку (процессор работает) но позволяет продолжить выполнение когда условие достигнуто. Проще всего QAtomicInt::testAndSetAcquire

Ну можно уснуть на немного и проверить состояние, если состояние не выполнено, то можно снова уснуть на немного, ... пока не получим необходимое состояние.
"Так защищаться можно", но

1) Возникают проблемы "а немного - это сколько ?"  :)
2) Образуется неприятный "стык". Напр мы поставили условие true, а потом нужно опять false. Но неизвестно подхватила ли нитка наше true


Название: Re: Приостановить поток
Отправлено: alexman от Ноябрь 20, 2011, 16:04
Ну можно уснуть на немного и проверить состояние, если состояние не выполнено, то можно снова уснуть на немного, ... пока не получим необходимое состояние.
sleep "плохая" функция. :)
IMHO, почти всегда (если не всегда) можно обойтись без нее и получить более эффективный код.
Не могу спорить, так как сам ее не использовал.


Название: Re: Приостановить поток
Отправлено: qt_user от Ноябрь 20, 2011, 16:08
все сделал с помощью мьютекса, один поток делает lock(), а второй после условия тоже пытается lock(),
но мьютекс закрыт, после возникновения события "продолжить" главный поток делает unlock(), думаю
дальше идея ясна


Название: Re: Приостановить поток
Отправлено: alexman от Ноябрь 20, 2011, 16:08
1) Возникают проблемы "а немного - это сколько ?"  :)
2) Образуется неприятный "стык". Напр мы поставили условие true, а потом нужно опять false. Но неизвестно подхватила ли нитка наше true
1. Эмпирически :)
2. Это да. Ну как вариант можно конечно хранить массив значений состояния. В данной ситуации надо что-нибудь другое использовать.


Название: Re: Приостановить поток
Отправлено: Igors от Ноябрь 20, 2011, 16:25
все сделал с помощью мьютекса, один поток делает lock(), а второй после условия тоже пытается lock(),
но мьютекс закрыт, после возникновения события "продолжить" главный поток делает unlock(), думаю
дальше идея ясна
Так не пойдет. Правило: если нитка делает lock(), то она же (и только она) должна сделать unlock(). А если хотите делать из разных ниток - используйте семафоры


Название: Re: Приостановить поток
Отправлено: qt_user от Ноябрь 20, 2011, 18:09
Так не пойдет. Правило: если нитка делает lock(), то она же (и только она) должна сделать unlock(). А если хотите делать из разных ниток - используйте семафоры
конечно переделать под семафоры не составит труда, но у меня возникает вопрос: это правило
чисто "правило хорошего стиля" или у него есть какие-то предпосылки?


Название: Re: Приостановить поток
Отправлено: Igors от Ноябрь 20, 2011, 18:15
это правило чисто "правило хорошего стиля" или у него есть какие-то предпосылки?
Это не имеет никакого отношения к стилю - нитка захватившая mutex (mutual exсlusion) должна его же и освободить, иначе результат не определен

По поводу "идея ясна" и "не составит труда" - "многопоточное программирование" только на первый взгляд кажется простым, но это не так  :)


Название: Re: Приостановить поток
Отправлено: qt_user от Ноябрь 20, 2011, 18:37
"многопоточное программирование" только на первый взгляд кажется простым, но это не так  :)
так с этим никто и не спорит :)


Название: Re: Приостановить поток
Отправлено: qt_user от Ноябрь 20, 2011, 19:50
2) Эти операции атомарные, но чтение+запись такой переменной нужно защищать блокировками
хотелось бы еще такое узнать, а если доступ к переменной через ссылку или через указатель:
Код:
1. wState = &workState;
2. *wState = run;
3. if (*wState == stop) {}

2 и 3 также будет атомарно?


Название: Re: Приостановить поток
Отправлено: Igors от Ноябрь 20, 2011, 23:10
2 и 3 также будет атомарно?
Будет-то будет, но код в котором даже все операции атомарны может успешно рухнуть - потому что др нитка может вклиниться между операциями. Простой пример
Код
C++ (Qt)
int a. b;
..
if (a == b)
a = 0;
b = 100;
}  
 
Здесь все атомарно, но тем не менее придется это место защищать блокировкой. Начиная от момента когда выполнилось a == b до момента когда выполнится a = 0 могло произойти многое, др нитка(и) могли изменить  a и b как они хотели.
Цитировать
С Ларисой я действительно пил вино, но это было на пасху


Название: Re: Приостановить поток
Отправлено: qt_user от Ноябрь 20, 2011, 23:14
Будет-то будет, но код в котором даже все операции атомарны может успешно рухнуть - потому что др нитка может вклиниться между операциями. Простой пример
Код
C++ (Qt)
int a. b;
..
if (a == b)
a = 0;
b = 100;
}
 
Здесь все атомарно, но тем не менее придется это место защищать блокировкой. Начиная от момента когда выполнилось a == b до момента когда выполнится a = 0 могло произойти многое, др нитка(и) могли изменить  a и b как они хотели.
Пасиба, это мы усвоили :)


Название: Re: Приостановить поток
Отправлено: Igors от Ноябрь 20, 2011, 23:22
Пасиба, это мы усвоили :)
Ну то так кажется - на эти грабли наступают все и много-много раз  :)


Название: Re: Приостановить поток
Отправлено: JamS007 от Январь 03, 2012, 23:24
1. void QThread::sleep ( unsigned long secs )

лучше эту:
void QThread::yieldCurrentThread () [static]

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


Название: Re: Приостановить поток
Отправлено: qt_user от Январь 04, 2012, 00:33
1. void QThread::sleep ( unsigned long secs )

лучше эту:
void QThread::yieldCurrentThread () [static]

она более "приближенная к системе", заставит передать управление другому потоку чтобы не тратить такты процессора в холостую.
не очень понимаю смысл этой ф-ции.. эта ф-ция просто передает управление другому потоку на один квант времени? то есть
отдает 1 свой квант другому потоку?

В чем тогда разница QThread::yieldCurrentThread () и QThread::sleep("1 квант времени")?


Название: Re: Приостановить поток
Отправлено: JamS007 от Январь 04, 2012, 00:49
qt_user
yieldCurrentThread() передаст управление другой нити, и тем самым прекратит выполнение первой. Отличие от sleep() состоит в том, что в следующий раз поток будет "разбужен" когда этого захочет планировщик, а не по истечению интервала времени.

из доки:
void QThread::sleep ( unsigned long secs ) [static protected]
Forces the current thread to sleep for secs seconds.

1. Нет никаких гарантий, что функция sleep() приведет к передаче управления другому потоку. Гипотетически - так должно быть, и наверное так часто и есть, но все-же.
2. Вы не сможете указать функции sleep() квант времени меньше 1 секунды, да есть msleep() но там Вы не сможете указать квант времени меньше 1 милисекунды.
3. sleep() только запускает механизм, который будет проверять прошел ли заданный промежуток времени или нет. Ничего не сказано о том, как эта проверка осуществляться. Она может выполниться 1 раз (через указанный промежуток времени) или 10 раз... Соответственно, за каждым разом будут потрачены ресурсы системы на сопоставление текущего времени с намеченным для пробуждения. В Вашем случае можно сэкономить эти затраты, ведь у вас уже есть условие, к которому можно привязаться (блокировка по мьютексу, а не точное время).

Я даже встречал негативные отзывы в сторону boost::thread за функцию sleep(). Там она реализована таким образом, что сопоставляет время системы с запланированным для пробуждения, и если в ОС сменить время, к примеру, на день назад, то поток будет разбужен через день+интервал времени, заданный функции sleep(). Подозреваю, что в Qt также.


Название: Re: Приостановить поток
Отправлено: qt_user от Январь 04, 2012, 23:13
HaySayCheese
Спасибо большое, а то я много слышал что sleep плохо, но не видел да и не понимал как работает yieldCurrentThread()
насчет пункта 2. есть еще QThread::usleep ( unsigned long usecs ) для микросекунд


Название: Re: Приостановить поток
Отправлено: BRE от Январь 04, 2012, 23:24
HaySayCheese
Спасибо большое, а то я много слышал что sleep плохо, но не видел да и не понимал как работает yieldCurrentThread()
Ну скорее sleep плохо не потому, что он не передает (или передает) управление другому потоку, а в неэффективности самого ожидания.
Например, поток ожидает какие-то данные и пока их нет засыпает на одну секунду. Данные могут придти в любой момент, т.е. поток проверил и заснул, а в этот момент данные появились, поток все равно будет спать секунду, а только потом проснется и обнаружит данные для обработки, хотя могу бы уже секунду как работать.
Поэтому, лучше использовать специальные механизмы, которые могут пробудить поток в тот момент, когда данные появились, например, условные переменные (QWaitCondition).


Название: Re: Приостановить поток
Отправлено: thechicho от Январь 06, 2012, 17:38
я чтобы сделать паузу в потоках, делал как-то так

.h потока
public:
    bool условие;
поток::run()
{
QEventLoop loopPause;

if (условие) {
connect(this, SIGNAL(pause()), &loopPause, SLOT(quit()));
loopPause.exec();
}

}

слот потока::blabla()
{
    emit pause();
}

слот главного потока::клик_на_мышку_пауза()
{
    if(поток->условие) {
        поток->условие = false;
        emit pause();
    } else {
        поток->условие = true;
    }
}

слот главного потока::создаем_потоки()
{
     for (int i = 0; i < 100500; i++) {
         класспотока *поток = new класспотока(this);
         поток->условие = false;
         connect(this, SIGNAL(pause()), поток, SLOT(blabla()));
         поток->погнали!;
     }
}

потом раскидывал
if (условие) {
connect(this, SIGNAL(pause()), &loopPause, SLOT(quit()));
loopPause.exec();

перед затратными по времени операциями и усе.
запускал по 500 потоков, работало все ч0тко.)


Название: Re: Приостановить поток
Отправлено: thechicho от Январь 06, 2012, 17:52
а не чуток по-другому, ща глянул.

при создании потоков
connect(this, SIGNAL(pauseThreadTrueSignal()), thread, SLOT(pauseThreadTrueSlot()));
connect(this, SIGNAL(pauseThreadFalseSignal()), thread, SLOT(pauseThreadFalseSlot()));

connect(ui->pushButtonStart, SIGNAL(clicked()), this, SLOT(pauseSlot()));

void гуи поток::pauseSlot()
{
    if (isIconPlay) {
        emit pauseThreadTrueSignal();
    } else {
        emit pauseThreadFalseSignal();
    }
}



void поток::pauseThreadTrueSlot()
{
    //qDebug() << "pauseThread: true";
    pauseThread = true;
    if (workManual)
        emit labelStatusSignal("Пауза");
}

void поток::pauseThreadFalseSlot()
{
    //qDebug() << "pauseThread: false";
    pauseThread = false;
    emit pauseDisabledSignal();    
    if (workManual)
        emit labelStatusClearSignal();
}

if (pauseThread) {
     connect(this, SIGNAL(pauseDisabledSignal()), &loopPause, SLOT(quit()));
     loopPause.exec();
}

isIconPlay - эт условие в гуи потоке (bool). я картинку делал прост, тип play, pause (еще и stop делал).
короче работало все на ура. я хз, нафик заморачиваться с атомарными операциями, если так проще и работает без проблем. но дело ваше, конечно :)