Господа,прошу помощи...
Проблема такая:
Есть приложение на базе SDI каркаса MFC где класс вид содержит в себе несколько диалоговых
окон.Одно из таких окон содержит в качестве переменной указатель на объект класса CListCtrl,
который создается в конструкторе.
Заполнение этого списка происходит в отдельном потоке(поскольку количество элементов очень
велико,а блокировать интерфейс плохо).
Поток создается функцией AfxBeginThread,которой в качестве параметра пересылается указатель
на список.
Вопрос:как корректно завершить поток досрочно, если пользователь решил закрыть
приложение.
Пытался сделать следующим образом:
Создавал обект CEvent g_eventStop,в цикле потока,
перед очередным занесением элемента в список,делал проверку типа
if(WaitForSingleObject(g_eventStop,0)==WAIT_OBJECT_0) return 0;
А в функции OnClose диалога:
CMyDialog: nClose()
{
g_eventStop.SetEvent();
WaitForSingleObject(хендл на поток,INFINITE);
CDialog: nClose();
}
Все работает,если сделать просто пустой цикл,если же вставлять там новый элемент в список,
то подвисаем.
Где ошибка и как исправить
[skip]
H>Все работает,если сделать просто пустой цикл,если же вставлять там новый элемент в список, H>то подвисаем. H>Где ошибка и как исправить
Когда ты вставляешь элемент (InsertItem) идет SendMessage с LVM_INSERTITEM, а поскольку поток в Wait, сообщение не обрабатывается. Т.е. DeadLock, ибо SendMessage синхронный.
В догонку. Судя по всему ты юзаешь "The first form of AfxBeginThread", которая "creates a worker thread", т.е. без цикла обработки сообщений. Юзай "The second form", которая "creates a user-interface thread". Можно еще попробовать вместо InsertItem юзать PostMessage с LVM_INSERTITEM, ибо она не синхронная.
Здравствуйте Hrum, Вы писали:
H>Господа,прошу помощи... H>Проблема такая: H>Есть приложение на базе SDI каркаса MFC где класс вид содержит в себе несколько диалоговых H>окон.Одно из таких окон содержит в качестве переменной указатель на объект класса CListCtrl, H>который создается в конструкторе. H>Заполнение этого списка происходит в отдельном потоке(поскольку количество элементов очень H>велико,а блокировать интерфейс плохо). H>Поток создается функцией AfxBeginThread,которой в качестве параметра пересылается указатель H>на список. H>Вопрос:как корректно завершить поток досрочно, если пользователь решил закрыть H>приложение. H>Пытался сделать следующим образом: H>Создавал обект CEvent g_eventStop,в цикле потока, H>перед очередным занесением элемента в список,делал проверку типа H>if(WaitForSingleObject(g_eventStop,0)==WAIT_OBJECT_0) return 0; H>А в функции OnClose диалога: H>CMyDialog: nClose() H>{ H>g_eventStop.SetEvent(); H>WaitForSingleObject(хендл на поток,INFINITE); H>CDialog: nClose(); H>} H>Все работает,если сделать просто пустой цикл,если же вставлять там новый элемент в список, H>то подвисаем. H>Где ошибка и как исправить
По поводу подвисания тебе уже объяснили.
Одно из самых простых решений избежать блокировки — посылать сообщения не синхронно, а асинхронно. Т.е. вместо SendMessage пользовать PostMessage (или SendNotifyMessage).
У меня была такая задачка. Был диалог с ListBox'ом. Этот ListBox заполнялся из других потоков. Вот как это было решено:
// test1Dlg.h : header file#define WM_CUSTOM_ADDSTRING ( WM_APP )
class CTest1Dlg : public CDialog
{
public:
CTest1Dlg(CWnd* pParent = NULL); // standard constructor
~CTest1Dlg();
// ...private:
Talker *m_talker;
};
Забыл сказать: потоки юзают метод Say() единственного объекта класса Talker (можно было реализовать как singleton, но это слишком для такой малой задачи).
Кроме того предпочтительнее пользовать в потоках именно асинхронные сообщения, т.к. тогда потоки не будут ждать пробужения потока окна и возврата из SendMessage.
Здравствуйте Patalog, Вы писали:
P>[skip].. PostMessage с LVM_INSERTITEM, ибо она не синхронная.
Однако будет очень внимателен когда передаешь указатели в PostMessage —
к тому времени когда контроль обратится к этой памяти ее содержимое может уже изменится!
Или память вообще может быть уже освобожденна.
Вообще-то эти сообщения (LVM_*) как раз должны использоваться с SendMessage,
а в твоем случае лучше использовать контроль в ввиртуальном режиме(LVS_OWNERDATA), который как раз и предназначен для отображения больших объемов данных: Virtualizing List Views to Handle Large Amounts of Data
Здравствуйте Jack Osicilator, Вы писали:
JO>Вообще-то эти сообщения (LVM_*) как раз должны использоваться с SendMessage, JO>а в твоем случае лучше использовать контроль в ввиртуальном режиме(LVS_OWNERDATA), который как раз и предназначен для отображения больших объемов данных:
LVS_OWNERDATA и многопоточность в данном контексте имеют очень сомнительную близость. При использовании LVS_OWNERDATA сообщения LVN_GETDISPINFO будут слаться в поток, создавший окно. А у человека контрол заполняется строками из другого потока.
Здравствуйте MaximE, Вы писали:
ME>LVS_OWNERDATA и многопоточность в данном контексте имеют очень сомнительную близость. При использовании LVS_OWNERDATA сообщения LVN_GETDISPINFO будут слаться в поток, создавший окно. А у человека контрол заполняется строками из другого потока.
LVS_OWNERDATA и многопоточность не только не близки, а вообше противопоставляются друг другу.
Когда используется LVS_OWNERDATA контрол не заполняется данными поэтому второй поток не нужен,
данные запрашиваются контролем только для тех строк которые будут отображены — и ни какого длительного копирования не надо.
Здравствуйте Jack Osicilator, Вы писали:
JO>LVS_OWNERDATA и многопоточность не только не близки, а вообше противопоставляются друг другу. JO>Когда используется LVS_OWNERDATA контрол не заполняется данными поэтому второй поток не нужен, JO>данные запрашиваются контролем только для тех строк которые будут отображены — и ни какого длительного копирования не надо.
Все правильно. Но чел спрашивал про многопоточность.
Здравствуйте MaximE, Вы писали:
ME>>Все правильно. Но чел спрашивал про многопоточность.
ME>Челу нужно решение, а не общие слова. Для них есть раздел общее.
Это альтернативное решение — многопоточность не единственный вариант, и даже может не лучший в данном случае.
Или вопрос именно о многопоточности, а не конкретно о заполнении ListControl-a?
Тогда почему в MFC форум?
Здравствуйте Hrum,
Уж если вы совсем программу собрались закрывать, то я думаю особенно то церемониться с нитью не стоит ...
AfxBeginThread возвращает указатель на нить (CWinThread*) и эту нить можно завершить просто и элегантно TerminateThread(Thread, 0);
А огород из мьютексов и прочего городить не стоит наверное
Здравствуйте Vasiliy_Krasnokutsky, Вы писали:
VK>Уж если вы совсем программу собрались закрывать, то я думаю особенно то церемониться с нитью не стоит ... VK>AfxBeginThread возвращает указатель на нить (CWinThread*) и эту нить можно завершить просто и элегантно TerminateThread(Thread, 0);
TerminateThread — это не есть просто и элегантно. Перед ее использованием стоит подумать на тему, "А что будет с программой, если поток, который я останавливаю с помощью TerminateThread, выполняет сейчас оператор new или функцию malloc?"
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: Завершение потока
От:
Аноним
Дата:
16.09.02 10:20
Оценка:
Здравствуйте Vasiliy_Krasnokutsky, Вы писали:
VK>Здравствуйте Hrum, VK>Уж если вы совсем программу собрались закрывать, то я думаю особенно то церемониться с нитью не стоит ... VK>AfxBeginThread возвращает указатель на нить (CWinThread*) и эту нить можно завершить просто и элегантно TerminateThread(Thread, 0); VK>А огород из мьютексов и прочего городить не стоит наверное
Ну по описанию ядра Windows эта функция самая неэлегантная