Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Литература_1 / photon_old.doc
Скачиваний:
38
Добавлен:
02.04.2015
Размер:
7.88 Mб
Скачать
      1. Модальные операции и потоки

Модальная операция – это та, где Вам надо ожидать появления какого-то конкретного события перед тем, как Вы можете начать исполнение – например, когда Вы хотите, чтобы пользователь принял решение и нажал кнопку "Да" или "Нет". Поскольку обычно до того, как появится ожидаемое, обычно придут другие события, Вам надо гарантировать, что они обработаны.

В однопоточном приложении прикрепите ответную реакцию к кнопкам "Да" и "Нет". В этой ответной реакции вызовите PtModalUnblock(). Когда Вы отобразите диалог, вызовите функцию PtModalBlock(). Эта функция запустит петлю обработки событий, похожую на PtMainLoop(), за исключением того, что функция PtModalBlock() возвращает управление, когда что-нибудь (например, ответная реакция, прикреплённая к кнопкам "Да" и "Нет"), вызовет PtModalUnblock().

В многопоточном приложении функция PtModalBlock() может:

  • делать то же, что и однопоточном приложении

или

  • блокироваться на переменной состояния и позволить другим потокам Photon'а обрабатывать события.

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

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

Если Вы знаете, что Ваше приложение не имеет двух несвязанных модальных операций, которые могут выполняться одновременно, но могут завершаться в любом порядке, Вы можете передать признак Pt_EVENT_PROCESS_ALLOW в функцию PtModalBlock(). Это указывает функции PtModalBlock() запустить петлю событий, даже если Вы имеете другие доступные Photon'овские потоки, и может уменьшить общее число Photon'овских потоков, которые нужны Вашему приложению.

      1. Завершение многопоточной программы

Завершение многопоточного приложения может оказаться мудрёным; вызов функции exit() сделает так, что Ваши потоки просто исчезнут, так что Вам следует убедиться, что Вы не завершите работу, пока другой поток делает что-то такое, что нельзя прервать, например, сохраняет файл.

 Не вызывайте pthread_exit() в потоке, который запер библиотеки Photon'а. Если вы сделаете это, в Вашем приложении будет утечка памяти.

Помните, что все ответные реакции выполняются потоком, который запер библиотеки.

В приложении Photon'а библиотека может вызвать функцию PtExit(), когда закрывается последнее окно Вашего приложения. Если вы не хотите, чтобы это случилось, пока поток выполняет что-нибудь важное, сбросьте признак Ph_WM_CLOSE в ресурсе Pt_ARG_WINDOW_MANAGER_FLAGS Вашего базового окна и обработайте сообщение о закрытии самостоятельно. Вам также необходимо найти все вызовы exit() или PtExit() в Вашем программном коде и принять меры, чтобы Вы не завершили работу до тех пор, пока это не станет безопасным. Если виджет в Вашем базовом окне имеет ответную реакцию типа Done или Cancel, Вы также должны это обработать.

Библиотека Photon'а предлагает несколько механизмов, чтобы сделать обработку этого типа ситуации проще и безопаснее:

  • Это простой счётчик, который вынуждает блокироваться функцию PtExit() до тех пор, пока он не станет равен нулю.

Функции, предоставляющие этот счётчик, PtPreventExit() и PtAllowExit(), являются не только потоко-безопасными, но также безопасными в смысле реального времени: они гарантируют выполнение ограниченного объёма машинного кода и никогда не генерируют инверсию приоритета.

Этот механизм считается относительно низкоуровневым и предназначен прежде всего для потоков, которые ничего не делают с функциями Photon'а (возможно, временно – т.е. пока находятся внутри секции PtLeave()/PtEnter() ).

Основанием является то, что определённые вызовы функций Photon'а, которые обычно являются блокирующими, просто завершают вызывающий поток, если висит PtExit() (в противном случае функция PtExit() будет потенциально надолго блокировать). Это также случается, когда поток блокируется перед тем, как другой поток вызывает PtExit(); блокированный поток завершается без возвращения из блокированного вызова. Список вызовов Photon'овских функций, которые явялются "летальными" после того, как другой поток вызвал PtExit(), включает попытки обработки событий, выполнение чего-либо модального, блокирования на переменной состояния с использованием функций PtCondWait() или PtCondTimedWait(), или вызовов PtEnter() или PtLeave().

  • Иногда может оказаться трудным гарантировать, чтобы Ваш поток не вызывал ничего из этого после вызова PtPreventExit() – и если это делается и убивается без возможности вызова PtAllowExit(), Ваш процесс будет заперт и Вы должны его убить.

Чтобы избежать подобных ситуаций, имеется флаг Pt_DELAY_EXIT, который Вы можете передать функции PtEnter() или PtLeave(). Выполнение этого не только помешает функциям PtEnter() или PtLeave() завершить Ваш поток, когда другой поток вызовет PtExit(), но также вызовет неявно PtPreventExit(). Если Ваш поток по какой-либо причине помирает, библиотека знает, что для Вас надо вызвать PtAllowExit(). Флаг Pt_DELAY_EXIT делает Вашу ответную реакцию "сохранить файл" столь простой:

my_callback( ... ) {

PtLeave( Pt_DELAY_EXIT );

save_file(); /* Здесь Вы в безопасности… */

PtEnter( 0 ); /* Но это может убить Вас ­– и это хорошо! */

}

Кроме того, Вы должны обеспечить, чтобы save_file() не пыталась выполнить какие-либо "летальные" вызовы. В частности, Вы не можете поднять всплывающий диалог с сообщением об ошибке, если что-то пошло неправильно. Если Вы хотите поднять всплывающий диалог, который потенциально займёт экран на минуты или часы, Вы должны сделать это перед вызовом PtExit(), например, использованием приёма с Pt_ARG_WINDOW_MANAGER_FLAGS, обсуждённого выше.

Чтобы завершить поток, который выполняет PtMainLoop(), без прекращения работы приложения в целом, вызывайте PtQuitMainLoop().

 Не вызывайте функцию PtMainLoop() в потоке, который не запер библиотеки Photon'а.

Если Вы вызываете PtQuitMainLoop() из главного потока Вашего приложения, приложение прекращает свою работу. Чтобы определить, находитесь ли Вы в главном потоке или нет:

  • Вызовите функцию pthread_self() из функции инициализации Вашего приложения и сохраните идентификатор потока в глобальной переменной.

  • Перед вызовом PtQuitMainLoop() вызовите pthread_self() вновь и сравните возвращённый идентификатор потока с глобальной переменной. Если идентификаторы потоков различны, вызов PtQuitMainLoop() будет прекращать выполнение потока, а не приложения.

Соседние файлы в папке Литература_1