crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

3 страниц V  < 1 2 3 >  
Ответить в данную темуНачать новую тему
> QMap + QThread
AXELman4ever
  опции профиля:
сообщение 31.10.2011, 13:47
Сообщение #11


Студент
*

Группа: Участник
Сообщений: 31
Регистрация: 22.9.2011
Пользователь №: 2902

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(ssoft @ 31.10.2011, 7:46) *
Если обращаться к объекту через его копию ( QMap object ), тогда у каждого потока будет свой собственный экземпляр данных никак не связанных с данными другого потока. Креша не будет, но данные будут разные.


Так, понял. Но это в случае если метод, в котором осуществляется доступ к QMap будет выполнен в разных потоках. А вот когда оба потока вызовут метод на выполнение в главном потоке, а не каждый в своем, то ведь как не крути - очередь у главного потока одна, и все запросы на изменение объекта QMap буду выполнятся по-очередно, ведь так?

К примеру, 2 потока отправляют сигнал с параметрами на слот главного потока.

Сообщение отредактировал AXELman4ever - 31.10.2011, 13:48
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ssoft
  опции профиля:
сообщение 31.10.2011, 13:53
Сообщение #12


Участник
**

Группа: Участник
Сообщений: 130
Регистрация: 17.2.2010
Из: Москва
Пользователь №: 1470

Спасибо сказали: 30 раз(а)




Репутация:   3  


Цитата(AXELman4ever @ 31.10.2011, 14:47) *
Так, понял. Но это в случае если метод, в котором осуществляется доступ к QMap будет выполнен в разных потоках. А вот когда оба потока вызовут метод на выполнение в главном потоке, а не каждый в своем, то ведь как не крути - очередь у главного потока одна, и все запросы на изменение объекта QMap буду выполнятся по-очередно, ведь так?


Нет, не так.
Вызов методов QMap прямой и синхронный и каждый в своем потоке, а не через очередь сообщений.

Цитата(AXELman4ever @ 31.10.2011, 14:47) *
оба потока вызовут метод на выполнение в главном потоке, а не каждый в своем


А это как возможно? Если только главный поток содержит некий диспетчер для QMap.))

Цитата(AXELman4ever @ 31.10.2011, 14:47) *
К примеру, 2 потока отправляют сигнал с параметрами на слот главного потока.


Тогда QMap не является общими данными для разных потоков, а является данными для главного потока.
Смотря как нужно для реализации.

Пусть изменения можно внести и так, а как быть с получением данных от QMap??? Или данные QMap в разных потоках не нужны, тогда зачем вообще городить огород с потоками, когда можно все реализовать в главном потоке???

Сообщение отредактировал ssoft - 31.10.2011, 14:01
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AXELman4ever
  опции профиля:
сообщение 31.10.2011, 19:11
Сообщение #13


Студент
*

Группа: Участник
Сообщений: 31
Регистрация: 22.9.2011
Пользователь №: 2902

Спасибо сказали: 0 раз(а)




Репутация:   0  


1:
Уточню:

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


Хочу привести начальный пример. Я покаместь опущу использование Q(Multi)Map, по скольку понимаю что проблема заключается в непонимании принципа вызова методов из потока.

Очень прошу Вас немного помучаться со мной :rolleyes:


Существует слот в главном потоке:
void doSmthing()
{
   qDebug() << QThread::currentThread();
}


существует 1 дочерний поток в котором run() описан следующим образом:

void run()
{
   while(true){
       msleep(150);    
       emit tryToDoSmth();
   }
}


в главном треде инициализирован дочерний поток и описан коннект для него:

MyThread *thread = new MyThread();
thread->start();        //запускаем очередь потока

connect(thread, SIGNAL(tryToDoSmth()), this, SLOT(doSmthing()));


Так вот, если в коннекте указать Qt::DirectConnection то согласно QThread::currentThread() выполнение метода произойдет в дочернем потоке, а при Qt::QueuedConnection - в главном. Разве не так?

И вот как я понимаю это, есть доступ прямой - когда много потоков вызывают 1 метод не принадлежащий ему, и этот метод выполняет главный поток, и синхронный - когда выполнение метода переносится в другие потоки которые выполняют его синхронно по отношению друг к другу.

2:
Задача подразумевает что: QMap принадлежит только(одному) главному потоку, и существует еще 10 потоков, которые дергают метод главного потока сигналом с параметрами. То есть главный поток все время получает параметры на слот и вкладывает их в QMap.

Так вот, вопрос в следующем: Вызов методов QMap в ситуации как я описал ранее, в 1 пункте этого поста - в любом случае будет принадлежать чужому потоку?Независимо от того стоит там Qt::DirectConnection или же Qt::DirectConnection, всеравно метод insert для QMap выполнится в чужом потоке?


UDP: Я прошу прощения, не ругайте сильно за чушь, если где была такая. Все мы были такими, все чего-то не знали или не понимали. Просто хотелось бы понять что к чему в подробностях, а не по принципу "ну раз работает - значит так надо" :)
Спасибо.

Сообщение отредактировал AXELman4ever - 31.10.2011, 20:58
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ssoft
  опции профиля:
сообщение 1.11.2011, 8:46
Сообщение #14


Участник
**

Группа: Участник
Сообщений: 130
Регистрация: 17.2.2010
Из: Москва
Пользователь №: 1470

Спасибо сказали: 30 раз(а)




Репутация:   3  


В такой реализации скрыта серьезная ошибка.
Правда ее допускают вначале практически 95% программистов.

Объясняю:

Объект QThread - это класс "обертка" вокруг реального потока.
Как и любой объект QObject он принадлежит тому потоку, в котором он создан, в данном случае главному потоку.

Сигнал

emit tryToDoSmth();


в данном случае опасен, т.к. он принадлежит объекту из главного потока, а вызывается в дочернем.
Поэтому здесь Qt::AutoConnection == Qt::DirectConnection и метод объекта главного потока будет вызываться напрямую из дочернего потока.

Если написать явно Qt::QueuedConnection, то проблем быть не должно, тюк. метод будет вызываться через очередь.

Цитата
И вот как я понимаю это, есть доступ прямой - когда много потоков вызывают 1 метод не принадлежащий ему, и этот метод выполняет главный поток, и синхронный - когда выполнение метода переносится в другие потоки которые выполняют его синхронно по отношению друг к другу.


Методы не принадлежат потокам).

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

Сделаю еще несколько замечаний:

1. Так писать не нужно, т.к. поток никогда корректно не завершиться.

void run()
{
   while(true){
       msleep(150);    
       emit tryToDoSmth();
   }
}


Нужно либо так

void run()
{
   while( not_stoped ){
       msleep(150);    
       emit tryToDoSmth();
   }
}

void setStopFlag ( bool yes )
{
    not_stoped = yes;
}


Либо так

void run()
{
    QTimer notifier; //  notifier принадлежит дочернему потоку, this принадлежит главному
    connect( &timer, SIGNAL( timeout() ), this, SIGNAL( tryToDoSmth() );
    notifier.start( 150 );
    exec();
}


2. В инициализации дочернего потока

MyThread *thread = new MyThread();


не указан родитель, т.о. thread никогда не удалиться (будут лики памяти)
Можно записать так

MyThread *thread = new MyThread( QCoreApplication::instance() );


3. Здесь очередь не запускается

thread->start();        //запускаем очередь потока


Очередь запускается только методом exec().

Сообщение отредактировал ssoft - 1.11.2011, 8:48
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AXELman4ever
  опции профиля:
сообщение 1.11.2011, 13:33
Сообщение #15


Студент
*

Группа: Участник
Сообщений: 31
Регистрация: 22.9.2011
Пользователь №: 2902

Спасибо сказали: 0 раз(а)




Репутация:   0  


Премного благодарен за толковое разъяснение.

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

2. Тоже самое могу сказать и про отсутствие указателя на родителя в объекте-наследнике QThread, по причине что я не задавался целью удалять объект в процессе работы программы, он ведь сам удалится по её завершению. К тому же, можно исправить ситуацию через delete, ему ведь все равно, есть у объекта родитель или его нет.

Спасибо, очень достойное замечание, уверен что я бы сделал ошибку будь у меня иной пример.

По 3 замечанию всё понятно ^_^


Но появилось еще несколько неясностей:

Цитата
Сигнал

emit tryToDoSmth();



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


Так именно это мне и требуется. А в чем заключается опасность?

Цитата
... Qt::AutoConnection == Qt::DirectConnection и метод объекта главного потока будет вызываться напрямую из дочернего потока.


"Метод объекта главного потока будет вызываться напрямую из дочернего потока", а выполняться он будет там же (в дочернем), или он оттуда будет только вызываться, а выполняться будет через очередь?

Цитата
Прямой вызов - это непосредственный вызов слота, как-будто бы метод вызывается явно. Поэтому где он вызван, в том потоке он и выполняется.


А где он вызван? Там, откуда испускается сигнал? А ведь сигнал принадлежит объекту, который в свою очередь принадлежит главному потоку, и согласно выше сказанному, сигнал уже не имеет отношения к дочернему потоку, а может только испускаться из него - это никак не влияет на то, где выполнится метод?






Блин, чем глубже я копаю, тем больше сомневаюсь в том, что я хоть что-то понимал :(

Сообщение отредактировал AXELman4ever - 1.11.2011, 14:49
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ssoft
  опции профиля:
сообщение 1.11.2011, 16:45
Сообщение #16


Участник
**

Группа: Участник
Сообщений: 130
Регистрация: 17.2.2010
Из: Москва
Пользователь №: 1470

Спасибо сказали: 30 раз(а)




Репутация:   3  


Цитата(AXELman4ever @ 1.11.2011, 14:33) *
"Метод объекта главного потока будет вызываться напрямую из дочернего потока", а выполняться он будет там же (в дочернем), или он оттуда будет только вызываться, а выполняться будет через очередь?


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

Очереди сообщений и реализованный через них механизм сигнал-слот взаимодействия - это все проделки Qt.

Цитата(AXELman4ever @ 1.11.2011, 14:33) *
А ведь сигнал принадлежит объекту, который в свою очередь принадлежит главному потоку


Сигналы и слоты - это обычные методы классов С++.
В С++ объекты не принадлежат никаким потокам.
Сопоставление QObject конкретному потоку реализовано непосредственно внутри Qt, но перехватывать вызов методов и перенаправлять их в очередь сообщений Qt не умеет.
Поэтому в каком потоке вызываем метод - в таком и выполняем (хоть сигнал, хоть слот, хоть обычный метод).

Цитата(AXELman4ever @ 1.11.2011, 14:33) *
А в чем заключается опасность?

Опасность в одновременном не синхронизированном доступе к данным из разных потоков, и как следствие возможном нарушения их целостности и крешу программы.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AXELman4ever
  опции профиля:
сообщение 1.11.2011, 18:23
Сообщение #17


Студент
*

Группа: Участник
Сообщений: 31
Регистрация: 22.9.2011
Пользователь №: 2902

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата
Опасность в одновременном не синхронизированном доступе к данным из разных потоков, и как следствие возможном нарушения их целостности и крешу программы.


Спасибо, я так и предполагал. А синхронизировать доступ можно через выше перечисленные Вами приблуды. Просто требовался подтверждение этому из Ваших "уст". ^_^

1:
Цитата
И выполняться он будет, там же, в дочернем потоке.
Где методы вызываются, там они и выполняются.

2:
Цитата
но перехватывать вызов методов и перенаправлять их в очередь сообщений Qt не умеет.


Но ведь (1) справедливо только для прямого вызова (используя Qt::DirectConnection), а если вести речь о вызове через сигнал/слот используя Qt::QueuedConnection?

Ведь документация гласит о Qt::QueuedConnection что при таком подключении The slot is executed in the receiver's thread. Как я понимаю это имеет прямое отношение к пункту 2.

Об очереди у меня сформировалось такое понятие - что каждый (не прямой) вызов метода из другого потока становится в очередь на выполнение в главном потоке. И когда главный поток берет на себя управление, он пересматривает очередь и выполняет в своем потоке все те задачи на выполнение, которые набежали от дочерних потоков.

Если мое понимание не есть верным, очень прошу Вас, докажите мне что это так. Спасибо.

Сообщение отредактировал AXELman4ever - 1.11.2011, 18:24
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ssoft
  опции профиля:
сообщение 2.11.2011, 17:17
Сообщение #18


Участник
**

Группа: Участник
Сообщений: 130
Регистрация: 17.2.2010
Из: Москва
Пользователь №: 1470

Спасибо сказали: 30 раз(а)




Репутация:   3  


Начиналось все с того, что

Цитата(AXELman4ever @ 27.10.2011, 13:21) *
1-ое:
Имеется 2 дочерних потока ("вытекающие" из мейн потока). Цель такова:

1 поток должен заносить значения в карту (QMap),
2 поток должен эти значения cчитывать.


Итак, если поток 2 должен иметь доступ к актуальным данным (даже на чтение), то без мьютексов не обойтись.

Цитата(AXELman4ever)
Но ведь (1) справедливо только для прямого вызова (используя Qt::DirectConnection), а если вести речь о вызове через сигнал/слот используя Qt::QueuedConnection?


Да все сработает, только нужно всегда помнить, что с сигналом tryToDoSmth() класса MyThread можно коннектится только как Qt::QueuedConnection. И обязательно указывать это в сопроводительной документации, если кто-то этим классом кроме Вас будет пользоваться. Просто не удобно.

Цитата(AXELman4ever)
Об очереди у меня сформировалось такое понятие - что каждый (не прямой) вызов метода из другого потока становится в очередь на выполнение в главном потоке. И когда главный поток берет на себя управление, он пересматривает очередь и выполняет в своем потоке все те задачи на выполнение, которые набежали от дочерних потоков.


Если соединение между сигналом и слотом выполнено с параметром Qt::QueuedConnection (в случае когда объекты связаны с разными потоками Qt::AutoConnection == Qt::QueuedConnection),

QObject::connect( obj1, SIGNAL( doFoo() ), obj2, SLOT( foo() ), Qt::QueuedConnection );

obj1 связан с thread1
obj2 связан с thread2

то при вызове сигнала doFoo() объекта obj1 формируется сообщение о необходимости вызова слота foo() у объекта obj2.
Это сообщение потоком thread1 помещается в очередь потока thread2. Когда thread2 доходит до обработки этого сообщения, тогда и вызывается слот foo().
Так что поняли, вроде, как правильно.

Просто ранее было написано, что к данным необходим доступ на чтение из дочернего потока.

Сообщение отредактировал ssoft - 2.11.2011, 17:18
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AXELman4ever
  опции профиля:
сообщение 2.11.2011, 23:13
Сообщение #19


Студент
*

Группа: Участник
Сообщений: 31
Регистрация: 22.9.2011
Пользователь №: 2902

Спасибо сказали: 0 раз(а)




Репутация:   0  


Замечательно, вижу свет в конце тоннеля ^_^ За что, Вам спасибо!

Хочу еще уточнить касательно мьютексов, на своем же примере.

Из множества потоков производится вызов метода на запись в экземпляр QMap, и всего 1 поток вызывает метод на их считывание по одному элементу с периодичностью в 1 секунду. После того как запись была изъята - этот элемент удаляется.

То есть, если не лочить процесс изъятия элемента из карты, то вызванный метод ненароком может прочесть 1 элемента, а изъять совсем другой?

А если элемент не извлекать вовсе, а только считывать его? Каким образом отсутствие мьютекса, в таком случае, может вызвать краш?

Сообщение отредактировал AXELman4ever - 3.11.2011, 0:24
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ssoft
  опции профиля:
сообщение 3.11.2011, 7:45
Сообщение #20


Участник
**

Группа: Участник
Сообщений: 130
Регистрация: 17.2.2010
Из: Москва
Пользователь №: 1470

Спасибо сказали: 30 раз(а)




Репутация:   3  


Цитата(AXELman4ever @ 3.11.2011, 0:13) *
То есть, если не лочить процесс изъятия элемента из карты, то вызванный метод ненароком может прочесть 1 элемента, а изъять совсем другой?

Верно абсолютно.

Цитата(AXELman4ever @ 3.11.2011, 0:13) *
А если элемент не извлекать вовсе, а только считывать его? Каким образом отсутствие мьютекса, в таком случае, может вызвать краш?


Предположим поток 1 записывает данные, а поток 2 считывает.
Запись данных приводит к вызову метода, внутри которого изменяются сами данные и структура их хранения.

Например, в реализации Qt для QMap сначала физически удаляются данные (вызывается деструктор), связанные с элементом, а затем правится структура QMap.

Так вот, без мьютекса, если метод чтения потока 2 будет вызван после удаления данных потоком 1, но до изменения структуры данных потоком 1, то это и может привести к крешу.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

3 страниц V  < 1 2 3 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 28.3.2024, 23:43