В предыдущем выпуске этой серии о том, как разработать приложение для Windows Phone с нуля, мы исправили некоторые проблемы, чтобы правильно запустить и обновить PeriodicTask . Теперь мы готовы выяснить, как приложение может взаимодействовать со своим PeriodicTask.
В сегодняшнем эпизоде EvenTiles вы узнаете, почему нельзя просто использовать переменную для связи между приложением и PeriodicTask. На мой взгляд, это важно, потому что понимание того, почему некоторые вещи не работают, делает вас лучшим разработчиком для Windows Phone и снижает вероятность того, что вам будет сложно отследить ошибки в ваших собственных приложениях. Чтобы разделить функциональность для передачи данных между приложением и его PeriodicTask, мы собираемся создать новый проект внутри решения EvenTiles.
Проект будет иметь тип Windows Phone Class Library, и мы будем называть его
EvenTilesComm . В своей первоначальной версии эта библиотека классов будет содержать один единственный класс с именем
TileData . Он используется для хранения текста, который пользователь может ввести на странице настроек приложения EvenTiles, чтобы PeriodicTask отображал эту строку на обратной стороне вторичной плитки после ее создания. Чтобы изменить текст на обратной стороне вторичной плитки,
TileData также будет содержать логическую переменную, указывающую, какая строка должна отображаться на обратной стороне вторичной плитки (либо строка, переданная приложением, либо строка по умолчанию, определенная внутри PeriodicTask).
Чтобы иметь доступ к TileDataкак в приложении EvenTiles, так и в PeriodicTask, оно содержит два статических свойства. Строковое свойство для хранения / извлечения сообщения, определенного на странице настроек, и логическое свойство, определяющее, какая строка должна отображаться в PeriodicTask. Свойство string всегда пишется приложением и читается PeriodicTask. Логическая переменная записывается как приложением, так и PeriodicTask, но читается только PeriodicTask. Из-за того, что PeriodicTask и приложение выполняются внутри своего отдельного процесса в разных потоках, и, поскольку они работают совершенно несинхронно друг с другом, существует вероятность того, что они оба захотят получить доступ к одному из свойств TileData по адресу в то же время.Чтобы предотвратить эту потенциально опасную ситуацию (опасную, поскольку она может оставить свойства в неопределенном состоянии, когда один из двух разных потоков хочет получить к ним доступ, в то время как другой обновляет их), оба свойства защищеныMutex объект синхронизации. |
ПРИМЕЧАНИЕ . Следующая (начальная) реализация класса TileData не будет работать должным образом, но она показана здесь, чтобы помочь вам понять, что означает, что приложение и его PeriodicTask принадлежат друг другу, но полностью отделены друг от друга в одно и то же время. Вам придется немного подождать рабочего решения, потому что оно будет представлено в следующем эпизоде EvenTiles.
Потокобезопасный доступ к данным:
public class TileData { private static Mutex mtx = new Mutex(false, "MtxSync"); private static string secondaryBackContent; private static bool showDefaultSecondaryBackContent; public static string SecondaryBackContent { get { return RetrieveSecondaryBackContent(); } set { StoreSecondaryBackContent(value); } } public static bool ShowDefaultSecondaryBackContent { get { return RetrieveShowDefaultSecondaryBackContent(); } set { StoreShowDefaultSecondaryBackContent(value); } } private static string RetrieveSecondaryBackContent() { mtx.WaitOne(); try { return secondaryBackContent; } finally { mtx.ReleaseMutex(); } } private static void StoreSecondaryBackContent(string content) { mtx.WaitOne(); try { secondaryBackContent = content; } finally { mtx.ReleaseMutex(); } } private static bool RetrieveShowDefaultSecondaryBackContent() { mtx.WaitOne(); try { return showDefaultSecondaryBackContent; } finally { mtx.ReleaseMutex(); } } private static void StoreShowDefaultSecondaryBackContent(bool showDefaultContent) { mtx.WaitOne(); try { showDefaultSecondaryBackContent = showDefaultContent; } finally { mtx.ReleaseMutex(); } } }
Если вы посмотрите на исходный код класса TileData, вы увидите, что мы используем два свойства, через которые мы вызываем несколько частных методов для фактического хранения или извлечения данных. Вы также увидите, что мы определяем один Mutex внутри класса TileData. У этого Mutex есть имя, что означает, что его можно использовать для синхронизации потоков в нескольких процессах. Это важно, поскольку EvenTiles и его PeriodicTask будут выполняться в отдельных процессах. Если мы посмотрим на один из отдельных методов поиска, вы увидите, как используется один Mutex, определенный в TileData.
Получение данных:
private static string RetrieveSecondaryBackContent() { mtx.WaitOne(); try { return secondaryBackContent; } finally { mtx.ReleaseMutex(); } }
В этом методе мы обращаемся к переменной secondBackContent только тогда, когда метод WaitOne в нашем Mutex возвращает. Когда WaitOne возвращается, наш метод имеет эксклюзивный доступ к переменной secondBackContent, пока мы не вызовем метод ReleaseMutex в нашем Mutex. В этот момент другой поток, ожидающий того же Mutex, получит доступ. Каждый метод, который использует Mutex для получения доступа к защищенным данным, должен при любых обстоятельствах вызывать ReleaseMutex, когда это будет сделано. Вот почему мы добавляем блок try / finally. Код в блоке finally будет выполняться, даже когда генерируются исключения. Таким образом, мы предотвращаем наше приложение от потенциальных тупиковых ситуаций (когда несколько потоков ждут друг друга, чтобы освободить объект синхронизации, который никогда не освобождается).
Способ защиты данных для EvenTiles работает должным образом, что можно проверить, отладив приложение и преднамеренно пропустив вызов ReleaseMutex из приложения EvenTiles (что также подробно объясняется в сопроводительном видео). Это приведет к тому, что PeriodicTask всегда будет ждать Mutex, потому что он никогда не будет выпущен приложением. Это по крайней мере доказывает, что EvenTiles и его PeriodicTask используют один и тот же Mutex, которым они оба должны владеть, прежде чем смогут получить доступ к переменным.
Есть еще потенциальная проблема, потому что мы вызываем метод WaitOne без параметров, что означает, что мы будем ждать неопределенно долго, пока Mutex станет доступным. В нашем простом примере этого достаточно, но в реальном приложении вы, скорее всего, захотите подождать определенное время и принять меры по истечении времени ожидания.
Однако есть существенная проблема с текущей реализацией нашего недавно созданного класса TileData. Мы уже видели, что Mutex правильно распределяется между EvenTiles и его PeriodicTask, но как насчет общедоступных свойств. Следующий снимок экрана показывает вам, что не так с классом TileData прямо сейчас. Несмотря на то, что переменная virtualBackContent установлена приложением EvenTiles, похоже, что он не инициализируется, когда PeriodicTask извлекает переменную (оба через соответствующие свойства).
Поскольку наше приложение EvenTiles и его PeriodicTask работают в отдельных процессах, они полностью отделены друг от друга, что означает, что они получают свои собственные копии переменных, к которым обе они хотят получить доступ, даже если эти переменные определены в отдельном проекте. Это хорошо (хотя мы должны решить проблему), потому что в противном случае все переменные, используемые каждым процессом в телефоне, должны иметь уникальное имя. Следующее видео показывает EvenTiles в действии в сочетании с PeriodicTask. Он показывает, как работает Mutex и как происходит сбой передачи данных.
Чтобы иметь возможность экспериментировать с этой неправильной реализацией EvenTiles, особенно чтобы понять, как приложение взаимодействует с PeriodicTask, образец кода доступен для загрузки здесь .
Вместо того, чтобы использовать «простые» переменные для передачи данных между EvenTiles и его PeriodicTask, нам придется использовать IsolatedStorage и заставить их обмениваться данными через файл. Это будет темой следующего эпизода EvenTiles.
Источник: http://mstruys.com/2012/01/08/eventiles-from-start-to-finishpart-13/