Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: andrew.k от Октябрь 28, 2010, 13:09



Название: Обмен данными между потоками
Отправлено: andrew.k от Октябрь 28, 2010, 13:09
У меня есть несколько потоков.
Хочу реализовать передачу данных от одного потока к другому хочу реализовать так:
Код:
KProtocolThread: public QThread
{
public:
  void sendCommand( int code, const QByteArray &data );
protected slots:
  void sendCommandImplementation( int code, const QByteArray &data );
};

void KProtocolThread::sendCommand( int code, const QByteArray &data )
{
  QMetaObject::invokeMethod( this, "sendCommandImplementation", Qt::QueuedConnection,
              Q_ARG( int, code ),
              Q_ARG( QByteArray, data ) );
}


Для использования, в основном потоке вызываю, thread->sendCommand (где thread указатель на экземпляр класса потока), что помещает вызов слота sendCommandImplementation в очередь событий потока.

Вопросы:
1. Адекватный ли это способ.
2. Какие есть подводные камни?
3. Очень важно. События в очереди обрабатываются в том же порядке в котором поступили или могут в произвольном?


Название: Re: Обмен данными между потоками
Отправлено: Denjs от Октябрь 28, 2010, 22:49
в сторону... и чем вас не устраивает согнал-слотовое оединение объектов находящихся в разных потоках? ???


Название: Re: Обмен данными между потоками
Отправлено: Igors от Октябрь 29, 2010, 14:26
Вопросы:
1. Адекватный ли это способ.
2. Какие есть подводные камни?
3. Очень важно. События в очереди обрабатываются в том же порядке в котором поступили или могут в произвольном?
3) Ну если "вообще о событиях в очереди" - то нет, могут и не в том же. Но для посланных сигналовю как Вы делаете - да, в том же.  По поводу 1) и 2): способ вполне адекватный, но производительностью не отличается, особенно учитывая что массивы все равно будут копироваться. Через сигналы (вместо invokeMethod) можно записать проще (хотя это дело вкуса).


Название: Re: Обмен данными между потоками
Отправлено: andrew.k от Ноябрь 01, 2010, 04:13
Вопросы:
1. Адекватный ли это способ.
2. Какие есть подводные камни?
3. Очень важно. События в очереди обрабатываются в том же порядке в котором поступили или могут в произвольном?
3) Ну если "вообще о событиях в очереди" - то нет, могут и не в том же. Но для посланных сигналовю как Вы делаете - да, в том же.  По поводу 1) и 2): способ вполне адекватный, но производительностью не отличается, особенно учитывая что массивы все равно будут копироваться. Через сигналы (вместо invokeMethod) можно записать проще (хотя это дело вкуса).
через сигналы у меня сделано в одну сторону. в другую через invokeMethod. Потому что мне нужно посылать данные конкретному потоку. Через сигналы это сделать невозможно.


Название: Re: Обмен данными между потоками
Отправлено: Amigo_sa от Ноябрь 01, 2010, 11:17
через сигналы у меня сделано в одну сторону. в другую через invokeMethod. Потому что мне нужно посылать данные конкретному потоку. Через сигналы это сделать невозможно.
Почему невозможно, вы же конкретный объект подписываете на сигнал, т.е. вы можете
1. ненужные вам потоки на время заблокировать для получения сигналов (blockSignals)
2. использовать разные сигналы для каждого потока.
3. В сигнале передавать параметр - ID нужного потока, слот будет проверять, "свое" ли сообщение ему пришло..


Название: Re: Обмен данными между потоками
Отправлено: andrew.k от Ноябрь 02, 2010, 21:41
через сигналы у меня сделано в одну сторону. в другую через invokeMethod. Потому что мне нужно посылать данные конкретному потоку. Через сигналы это сделать невозможно.
Почему невозможно, вы же конкретный объект подписываете на сигнал, т.е. вы можете
1. ненужные вам потоки на время заблокировать для получения сигналов (blockSignals)
ужоснах!
Цитировать
2. использовать разные сигналы для каждого потока.
а как динамически создать N сигналов?
Цитировать
3. В сигнале передавать параметр - ID нужного потока, слот будет проверять, "свое" ли сообщение ему пришло..
Это мягко говоря очень неоптимально. Один сигнал получат сразу все потоки, хотя предназначается он только для одного.


Название: Re: Обмен данными между потоками
Отправлено: Igors от Ноябрь 02, 2010, 22:09
Это мягко говоря очень неоптимально. Один сигнал получат сразу все потоки, хотя предназначается он только для одного.
Сигнал получает объект (а не нитка). Если задан (явно или нет) QueuedConnection то сигнал будет доставляться через eventLoop той нитки которой этот объект "принадлежит". Эту принадлежность можно установить (moveToThread). Конечно надо крутить eventLoop для ниток, но как я понял, у Вас это есть. Неясно в чем проблема и зачем усложнять себе жизнь используя invokeMethod.


Название: Re: Обмен данными между потоками
Отправлено: andrew.k от Ноябрь 03, 2010, 02:26
Это мягко говоря очень неоптимально. Один сигнал получат сразу все потоки, хотя предназначается он только для одного.
Сигнал получает объект (а не нитка). Если задан (явно или нет) QueuedConnection то сигнал будет доставляться через eventLoop той нитки которой этот объект "принадлежит". Эту принадлежность можно установить (moveToThread). Конечно надо крутить eventLoop для ниток, но как я понял, у Вас это есть. Неясно в чем проблема и зачем усложнять себе жизнь используя invokeMethod.

Сервер создает на каждое вход подключение поток. На другой стороне создается один поток на исходящее подключение.
Вход и исход потоки один и тот же класс (работают одинаково).
Когда поток "насобирал" необходимое ему кол-во данных он эмитит сигнал с данными (сообщение).
Все сигналы от потоков сервера связаны с одинм его слотом. Тут проблем нет. Легко определить от кого пришли данные.
Но теперь мне нужно этому потоку с которого пришли данные послать ответ?
Как передать данные нужному потоку сигналами и слотами?


Название: Re: Обмен данными между потоками
Отправлено: Igors от Ноябрь 03, 2010, 15:04
Но теперь мне нужно этому потоку с которого пришли данные послать ответ?
Как передать данные нужному потоку сигналами и слотами?
Ну в любом случае Вам известен объект которому нужно передать. Я бы попробовал так:

- у объекта сделал ф-цию которая посылает сигнал самому себе (QueuedConnection).
- сервер просто вызывает эту ф-цию объекта (в нитке исполнения сервера), ф-ция кладет event в тот eventLoop что надо
- если есть опасность что 2 или более сервера вызовут этот метод, защититься мутексом в начале метода


Название: Re: Обмен данными между потоками
Отправлено: andrew.k от Ноябрь 03, 2010, 15:52
Но теперь мне нужно этому потоку с которого пришли данные послать ответ?
Как передать данные нужному потоку сигналами и слотами?
Ну в любом случае Вам известен объект которому нужно передать. Я бы попробовал так:

- у объекта сделал ф-цию которая посылает сигнал самому себе (QueuedConnection).
- сервер просто вызывает эту ф-цию объекта (в нитке исполнения сервера), ф-ция кладет event в тот eventLoop что надо
Собственно я так и сделал. через invokeMethod.
Как можно послать сигнал самому себе?
делать emit signal, а к сигналу коннектить слот? Мне кажется это через Опу. И по сути делать то, что делает invokeMethod, за исключением того, что invokeMethod - это логично и понятно, а эмит сигнала для самого себя - это странно.
Цитировать
- если есть опасность что 2 или более сервера вызовут этот метод, защититься мутексом в начале метода
Во-первых, двух серверов нет. Каждый сервер управляет только своими собственными потоками.
Во-вторых, кто бы не вызывал эту функцию, она просто помещает в очередь вызов слота. Не вижу в этом опасности.


Название: Re: Обмен данными между потоками
Отправлено: Igors от Ноябрь 03, 2010, 17:37
делать emit signal, а к сигналу коннектить слот? Мне кажется это через Опу. И по сути делать то, что делает invokeMethod, за исключением того, что invokeMethod - это логично и понятно, а эмит сигнала для самого себя - это странно.
А что тут странного? С QueuedConnection cигнал - тот же postMessage. А вызов postMessage самому себе встречается повсеместно (хоть в том же Вындоуз). "Оформление" получается приятнее, connect засунуть в конструктор, ну и просто позвать метод-сигнал, напр так
Код:
theProtocolThread->PostCommand(code, data);
Так что это столь же логично и через то же самое место  :)


Название: Re: Обмен данными между потоками
Отправлено: andrew.k от Ноябрь 03, 2010, 18:20
делать emit signal, а к сигналу коннектить слот? Мне кажется это через Опу. И по сути делать то, что делает invokeMethod, за исключением того, что invokeMethod - это логично и понятно, а эмит сигнала для самого себя - это странно.
А что тут странного? С QueuedConnection cигнал - тот же postMessage. А вызов postMessage самому себе встречается повсеместно (хоть в том же Вындоуз). "Оформление" получается приятнее, connect засунуть в конструктор, ну и просто позвать метод-сигнал, напр так
Код:
theProtocolThread->PostCommand(code, data);
Так что это столь же логично и через то же самое место  :)

Странно тем, что зная что сигнал будет обрабатываться тем же самым объектом. Генерить этот сигнал и внутри самого себя к нему коннектиться, если можно просто в нужном месте положить вызов слота в очередь.
В итоге emit sendCommand( code, data );
заменится на sendCommand( code, data );
Т.е. никакой разницы в использовании.

С другой стороны о вкусах не спорят :) По мне так мой метод нагляднее.
Вот например, смотрит другой человек твой хедер. видит там сигнал. он может подумать, что к нему надо коннектиться, что-то с этим делать. читать твои комментарии, чтобы понять как это работает. Можно по ошибке или непониманию (в случае другого программиста) сделать дисконнект и нарушить логику работы твоего класса.
В моей реализации только одна публичная функция, которая делает, что нужно. Понятно без комментариев. Реализация скрыта и недоступна. Инкапсуляция и ниибет :)


Название: Re: Обмен данными между потоками
Отправлено: andrew.k от Ноябрь 03, 2010, 18:43
честно говоря я так и не понял как положить вызов слота в очередь при помощи postEvent.


Название: Re: Обмен данными между потоками
Отправлено: Akon от Ноябрь 03, 2010, 22:57
QEvent::MetaCall  :)


Название: Re: Обмен данными между потоками
Отправлено: andrew.k от Ноябрь 04, 2010, 14:02
QEvent::MetaCall  :)
И что?