Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как сделать аналог SendMessage, именно для потока, а не для окна. 
V
    Опции темы
Dreamer_0x01
Дата 20.1.2006, 19:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Цитата(Fin @ 20.1.2006, 17:44 Найти цитируемый пост)

Кто мешает в потоке сделать примерно такой код.


Ну хочется мне именно так попробовать сделать, и посмотреть, что получится, что ж тут поделать?
smile


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 20.1.2006, 20:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Dreamer_0x01, извини, отвлекоась на семейные проблемы...
Если ты хочешь сделать так, как я написала, без окон, тебе надо перекрыть PumpMessage, а не PreTranslateMessage - потому что вторая вызывается ДО, а не после обработки сообщения.
Если стандартная PumpMessage возвращает TRUE, вызывай SetEvent.
Событие должно быть с автосбросом! Флаг нужно изменять атомарно! Не надо писать while (flag) в потоке!
Ну ладно, английский ты не любишь, но код MFC кто мешает посмотреть?!!!



--------------------
...
PM   Вверх
Dreamer_0x01
Дата 21.1.2006, 00:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Цитата(Earnest @ 20.1.2006, 20:53 Найти цитируемый пост)

Флаг нужно изменять атомарно!

Не понял, это что значит?



--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 22.1.2006, 09:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Это означает, что нужно гарантировать, что никто не обратится к флагу, пока операция изменения не завершена - та самая межпоточная безопасность. Для защиты более сложных объектов приходится использовать как минимум critical section, но для int'ов есть специальные функции Interlocked***. Тебе подойдет InterlockedExchange.


--------------------
...
PM   Вверх
Dreamer_0x01
Дата 22.1.2006, 15:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Earnest

Я делаю так(допустим, проверяю флаг FLAG1 внутри структуры MyStruct)
Код

typedef struct {
char FLAG1;
CRITICAL_SECTION cs;
//другие поля
}MYSTRUCT;


И в потоках делаю следующее:
Код

//при инициализации потока в него передано MYSTRUCT * MyStruct;
{
//какие-то действия...

//этап проверки состояния флага:

char Temp_Flag;
::EnterCriticalSection(&MyStruct->cs);
Temp_Flag=MyStruct->FLAG1;
::LeaveCriticalSection(&MyStruct->cs);

if(TempFlag==МОЯ_КАКАЯ_ТО_КОНСТАНТА)
{
//какие-то действия
}
//ну и так далее
}


Ты это имела в виду, или что-то принципиально другое?


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 22.1.2006, 19:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Это, это, только зачем так сложно...
Если речь идет только о защите флага, то проще (короче и нагляднее) использовать функции Interlocked. Например:
Код

struct CMyStruct
{
   ...
   LONG m_SyncFlag;   // LONG - потому что так хочет InterlockedExchange
};

Далее, в коде обработки собщений:

Код

if (InterlockedExchange(&pMyStruct->m_SyncFlag,0))
{
   // какие-то действия, видимо - SetEvent
}


В этом фрагменте InterlockedExchange возвращает текущее значение флага и записывает туда 0 (мы ведь хотим, чтобы флаг, поставленный в SendThreadMessage, сработал единожды). И все это делается гарантированно атомарно. Соответственно, устанавливать флаг нужно вызовом InterlockedExchange((&pMyStruct->m_SyncFlag,1).

Написала я этот код и подумала: здорово похоже на авто-событие. Там, может, событие и использовать? Еще одно. Т.е. примерно так:

Код

struct СMySync
{
   CEvent m_evSync;
   CEvent m_evReady;   
};

#define TIMEOUT 15000

LRESULT CMyThread::SendThreadMessage(...параметры...)
{
   m_pMySync->m_evSync.SetEvent();
   PostThreadMessage(...);
   m_pMySync->m_evReady.Lock(TIMEOUT); // то же самое, что WaitForSingleObject(m_pMySync->m_evReady,TIMEOUT);
   ...
}

Обработчик ()
{
   ...
   if (m_pMySync->m_evSync.Lock(0)) 
   {
      ...
      m_pMySync->m_evReady.SetEvent();
   }
}


PS
MFC-обертки объектов ядра удобнее и нагляднее "родных" функций API.


--------------------
...
PM   Вверх
takedo
Дата 23.1.2006, 08:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 501
Регистрация: 1.6.2005

Репутация: нет
Всего: 3



Dreamer_0x01
Во чего я подумал... Если у тебя в потоке нет окна, тогда вообще нет необходимости слать сообщения и вызывать страшные функции PreTranslateMessage и др. Создавай просто функцию bool func(); и ее вызывай. Это не будет отличатся от SendMessage, так как любое сообщение - суть вызов функции (в данном случае func()), ты ведь можешь её и сразу вызвать. А сообщения нужны лишь для того, чтобы не парится с синхронизацией, так как они обрабатываеются потоком, создавшим окно(посмотри ветку где куча народу спорила о синхронизации, я там пример из рихтера приводил, правда никто вроде бы не обратил внимания smile ) и только им => ничего и синхронизировать не надо. Но если ты шлешь мессаги из разных потоков, тогда можно и простыми объектами синхронизации обойтись smile Но вот уже четыре дня чешется рука тебе плюс поставить - за то что реализовал интерфейсный поток(мне вот лень было для пробы делать smile ) вот и ставлю, благо есть у меня ещё на енто право smile


--------------------
я не гольфист - я хоккеист
PM MAIL   Вверх
Dreamer_0x01
Дата 23.1.2006, 10:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



takedo
Спасибо за поддержку, вот уж сюрприз ;)

Цитата(takedo @ 23.1.2006, 08:13 Найти цитируемый пост)

А сообщения нужны лишь для того, чтобы не парится с синхронизацией,

Вот именно для этого.

Цитата(takedo @ 23.1.2006, 08:13 Найти цитируемый пост)

так как любое сообщение - суть вызов функции (в данном случае func()), ты ведь можешь её и сразу вызвать

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




--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
takedo
Дата 23.1.2006, 12:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 501
Регистрация: 1.6.2005

Репутация: нет
Всего: 3



Dreamer_0x01 Вообще-то когда ты вызываешь SendMessage() - да она ставится в очередь синхронных сообщений, но выйдешь ты из неё только тогда, когда отработает функция, которая будет вызвана обработчиком, вот её то и можно сразу вызвать обернув её в CriticalSec.Lock() CriticalSec.UnLock(); тогда в ней никого другого не будет (секцию объявляешь внутри функции да и статической пожалуй).
Добавлено @ 12:59
ну да ладно, делай как тебе удобнее, тут на вкус и цвет как говорится товарищей нет smile


--------------------
я не гольфист - я хоккеист
PM MAIL   Вверх
Dreamer_0x01
Дата 23.1.2006, 17:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Цитата(Earnest @ 22.1.2006, 19:13 Найти цитируемый пост)

Соответственно, устанавливать флаг нужно вызовом InterlockedExchange((&pMyStruct->m_SyncFlag,1).

Спасибо, про это я точно не мог знать.

Цитата(Earnest @ 22.1.2006, 19:13 Найти цитируемый пост)

Написала я этот код и подумала: здорово похоже на авто-событие. Там, может, событие и использовать? Еще одно. Т.е. примерно так:

Вещь получилась обалденная и работает! Круто!

"+" тебе за помощь в данном топике, моя идея начала потихоньку работать!

Но осталось победить еще одну тонкость. Допустим, у нас не два потока, а три. (допустим поток1, поток2 и поток3).
Поток 2 посылает потоку 1 синхронное сообщение, поток 1 начинает его обрабатывать, поток 2 ждет события. Все хорошо. Но в этот момент поток 3 тоже посылает синхронное сообщение потоку 1. И соответственно, он тоже замирает, и ждет установки события. Но беда в том, что когда поток 1 выполнит обработку сообщения потока 2, он установит событие, по которому разбудится не только поток 2, но и поток 3, что есть неправильно.

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

Что скажешь на этот счет?


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 23.1.2006, 19:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Скажу, что каждым потоком нужно управлять отдельно.
Цитата(Dreamer_0x01 @ 23.1.2006, 17:01 Найти цитируемый пост)

он установит событие, по которому разбудится не только поток 2, но и поток 3, что есть неправильно

Нет, разбудится только один поток (событие-то с автосбросом), но вот какой... это никак неопределено.

Правда, мы тут слегка путаем понятия. Есть поток, который thread, и есть поток -класс CMyThread. Все о чем мы говорили до этого, относится к классу CMyThread. А вот вызовы его методов происходят из разных потоков.
Назовем "родным" тот поток, который создается AfxBeginThread для класса CMyThread, а внешним - поток, который вызывает нашу гипотетическую ф-ю SendThreadMessage.
Надеюсь, ты как и я подразумевал, что она будет реализована как нестатический член класса CMyThread. Кстати, тогда флаги и\или события лучше тоже сделать его мемберами (нет никакого смысла заводить какие-то левые структуры).
Так вот, а обработка сообщений производится в "родном" потоке. Ну, это нам Windows обеспечит, а вот то, что SendThreadMessage должна вызываться из внешнего потка, неплохо бы проверить (иначе ты сам себе deadlock устроишь, да и смысла никакого нет).

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

Правда, не могу не согласится с takedo насчет того, что все это можно реализовать без интерфейсного потока, и, возможно, получится даже проще. Я, честно говоря, тоже считаю, что если окна в потоке не нужны, то лучше обойтись рабочим потоком.

Например, в одном из наших приложений, потоки устроены следующим образом.
Каждый поток создает обязательное событие для завершения и произвольное число событий (или других объектов синхронизации). Каждому событию сопоставляется обработчик. Функция потока - это цикл ожидания WaitForMultipleObjects + обработка того, что просигналило или выход. Естественно, все это делает базовый код, а производным классам остается только определить обработчики.

Спасибо тебе за +.



--------------------
...
PM   Вверх
Dreamer_0x01
Дата 23.1.2006, 22:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Цитата(Earnest @ 23.1.2006, 19:10 Найти цитируемый пост)

Надеюсь, ты как и я подразумевал, что она будет реализована как нестатический член класса CMyThread. Кстати, тогда флаги и\или события лучше тоже сделать его мемберами (нет никакого смысла заводить какие-то левые структуры).

Все так и сделано, это все я засунул в private-часть класса.
Цитата(Earnest @ 23.1.2006, 19:10 Найти цитируемый пост)

Ну, это нам Windows обеспечит, а вот то, что SendThreadMessage должна вызываться из внешнего потка, неплохо бы проверить

Проверил, вроде бы все нормально. Я создал приложение на основе диалога, в котором породил подобный поток, и запустил эту самую функцию. В обработчике сообщения вставил мессаджбокс.
В итоге во время появления события я получал тот самый мессажбокс, но что особо порадовало - то, что полностью блокировалось GUI диалога, то есть он был полностью "замороженный" до тех пор, пока я не кликал по мессаджбоксу. То есть тот поток, который создал мессаджбокс (то есть в твоей терминологии. наш "родной" поток), работал, а поток, которому принадлежало диалоговое окно (то бишь внешний) спал, что собственно мне и нужно было.

Цитата(Earnest @ 23.1.2006, 19:10 Найти цитируемый пост)

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

Не очень понял, про что это. Как это "обзаводится"?
Добавлено @ 22:45
Цитата(Earnest @ 23.1.2006, 19:10 Найти цитируемый пост)

Я, честно говоря, тоже считаю, что если окна в потоке не нужны, то лучше обойтись рабочим потоком.

Хе-хе-хе smile
Опять возвращаемся к мысли "А не создать ли нам фиктивное окно в потоке".

Тогда функция SendThreadMessage() будет просто делать SendMessage() для нашего "фиктивного" окошка, производного от CWnd, у этого окошка мы переопределим PreTranslateMessage(), в котором сделаем PostThreadMessage() его зозяину, то бишь нашему потоку ;)

Тогда механизм вызова функции SendThreadMessage() будет такой же, но вот накладные расходы на очереди сообщений, их ожидания и пр. возьмет уже система.... Главное в этом случае добиться того, чтобы оконная очередь сообщения обслуживалась именно нашим "родным" потоком...
smile



--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Dreamer_0x01
Дата 24.1.2006, 17:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



А вот еще подумал, можно действительно для каждого потока, использующего данную функцию, завести пару таких событий.
То есть завести массив структур, содержащих в себе два этих самых CEvent и указатель на CWinThread, по умолчанию равный NULL. (допустим, ограничим его для начала 16 потоками, ряд ли мне когда-либо больше понадобится) Когда какой-то поток будет вызывать эту функцию, я буду осущетсвлять поиск элемента массива с нулевым указателем, присваивать этому указателю значение AfxGetThread, и собственно выполнять вышеуказанные действия с событиями.
В обработчике события я буду делать WaitForMultipleObjects всех нужных событий, по коду события определять номер элемента ассива, сигналить второе событие, и очищать указатель на поток.

Как думаешь, это нормально, или похоже на безумие?


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Earnest
Дата 24.1.2006, 20:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 87
Всего: 183



Цитата(Dreamer_0x01 @ 23.1.2006, 22:31 Найти цитируемый пост)

Опять возвращаемся к мысли "А не создать ли нам фиктивное окно в потоке".

Да нет же, рабочий поток - этот как раз поток без очереди сообщений, и уж конечно без всяких окон. В противоположность интерфейстному.

Цитата(Dreamer_0x01 @ 24.1.2006, 17:52 Найти цитируемый пост)

То есть завести массив структур, содержащих в себе два этих самых CEvent и указатель на CWinThread

На фига тебе отдельный массив структур.
Заведи базовый класс потока, события - переменные класса, как и функция Send... и обработка.
И храни себе массив указателей на потоки.

Цитата(Dreamer_0x01 @ 24.1.2006, 17:52 Найти цитируемый пост)

Когда какой-то поток будет вызывать эту функцию, я буду осущетсвлять поиск элемента массива с нулевым указателем, присваивать этому указателю значение AfxGetThread, и собственно выполнять вышеуказанные действия с событиями.

"Эту" - это SendThreadMessage? Так ты в любом случае должен вызывать ее для конкретного потока. Т.е. клиент должен знать, к кому обращается. Иначе фигня какая-то получается.
Надо бы тебе почетче сформулировать, какую именно задачу ты решаешь. А то у меня создается впечатление, что ты хочешь создать систему управления потоками "вообще", на все случаи жизни.
Не бывает. smile Или бывает, но
Цитата(Dreamer_0x01 @ 24.1.2006, 17:52 Найти цитируемый пост)

похоже на безумие






--------------------
...
PM   Вверх
Dreamer_0x01
Дата 25.1.2006, 13:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Терминатор
**


Профиль
Группа: Участник
Сообщений: 780
Регистрация: 14.4.2005
Где: Санкт-Петербург

Репутация: 9
Всего: 12



Цитата(Earnest @ 24.1.2006, 20:15 Найти цитируемый пост)

На фига тебе отдельный массив структур.
Заведи базовый класс потока, события - переменные класса, как и функция Send... и обработка.
И храни себе массив указателей на потоки.

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


--------------------
Нет ничего невозможного. Есть цели, и есть время и силы на их достижение.
PM ICQ   Вверх
Страницы: (3) Все 1 [2] 3 
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Visual C++/MFC/WTL | Следующая тема »


 




[ Время генерации скрипта: 0.0593 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.