Могу ли я иметь несколько потоков GUI в MFC?
У меня есть большое приложение на основе MFC, которое включает в себя некоторые потенциально очень медленные задачи в основном потоке. Это может создать видимость того, что приложение зависло, когда оно фактически выполняет свою долгую задачу. С точки зрения удобства использования, я хотел бы дать пользователю больше отзывов о прогрессе и иметь возможность отменить задачу в чистом виде. Хотя выделение длинных задач в отдельные потоки было бы лучшим долгосрочным решением, я думаю, что прагматичным краткосрочным решением является создание нового потока графического интерфейса, инкапсулированного в своем собственном объекте, с диалогом, включающим индикатор выполнения и кнопку отмены, используемую в аналогично объекту CWait. Основной поток отслеживает состояние отмены с помощью метода IsCancelled и завершает работу с помощью throw, когда это необходимо.
Это разумный подход, и если да, то есть ли уже какой-нибудь код MFC, который я могу использовать, или я должен развернуть свой собственный? Первый эскиз выглядит так
class CProgressThread : public CWinThread
{
public:
CProgressThread(int ProgressMax);
~CProgressThread()
void SetProgress(int Progress);
BOOL IsCancelled();
private:
CProgressDialog *theDialog;
}
void MySlowTask()
{
CProgressThread PT(MaxProgress);
try
{
{
{ // deep in the depths of my slow task
PT.SetProgress(Progress);
if (PT.IsCancelled())
throw new CUserHasHadEnough;
}
}
}
catch (CUserHasHadEnough *pUserHasHadEnough)
{
// Clean-up
}
}
Как правило, я склонен иметь один поток с графическим интерфейсом и много рабочих потоков, но этот подход может сэкономить мне кучу рефакторинга и тестирования. Есть серьезные потенциальные подводные камни?
1 ответ
Короткий ответ: да, вы можете иметь несколько потоков GUI в MFC. Но вы не можете получить доступ к компоненту GUI напрямую, кроме созданного потока. Причина в том, что Win32 под MFC хранит обработчик GUI для каждого потока на основе. Это означает, что обработчик в одном потоке не виден другому потоку. Если вы перейдете к исходному коду класса CWinThread, вы можете найти там атрибут карты обработчика.
В Windows (MFC) нет большой разницы между рабочим потоком и потоком GUI. Любой поток может быть изменен на поток GUI после создания очереди сообщений, которая создается после первого вызова, связанного с сообщением, такого как GetMessage().
В приведенном выше коде, если индикатор выполнения создается в одном потоке, а MySlowWork() вызывается в другом потоке. Вы можете использовать только атрибуты CProgressThread, не касаясь функций, связанных с графическим интерфейсом Win32, таких как close, setText, SetProgress..., поскольку все они нуждаются в обработчике GUI. Если вы вызовете эти функции, ошибка не сможет найти указанное окно, так как этот обработчик отсутствует в отображении обработчика потока.
Если вам нужно изменить графический интерфейс, вам нужно отправить сообщение в ветку владельца этого индикатора. Пусть этот поток обрабатывает сообщение самостоятельно (обработчик сообщения) через PostThreadMessage, подробности см. В MSDN.