Your app’s task agent may be invoked even when your app is in the foreground. It is, therefore, important to ensure that simultaneous access to shared resources, such as files in isolated storage, is controlled and performed safely.
A System.Threading.Mutex
can be employed to synchronize critical sections of code—code that accesses a common resource—within your primary app and your background agent. A Mutex
is a synchronization primitive. It is similar to the Monitor
class, but with a scope that allows you to achieve interprocess synchronization, enabling you to synchronize across your foreground app and your background agent.
Unlike Silverlight for the browser, a Mutex
instance in Windows Phone must be named, and the scope of a Mutex
is limited to your primary app and its background agent.
Note
A Mutex
cannot be used to synchronize different apps, nor as a conduit for interapp communication.
Listing 32.11 shows an excerpt from the CommonResourceExample
class in the downloadable sample code that demonstrates how to read and write to a file in isolated storage in a safe manner, while preventing another thread, such as from your background agent, from accessing the file simultaneously.
When the ReplaceFileContents
method is called, WaitOne
is called on the Mutex
, which blocks if the Mutex
is owned by another thread in your foreground app or a background agent. If not owned, or when released by another thread, the contents of a file are read, and then overwritten with a new content string. The result is returned to the caller. By using a Mutex
, the result is guaranteed to be the content of the file immediately prior to writing the new content.
public class CommonResourceExample
{
static readonly Mutex mutex = new Mutex(false, "CommonResourceExample");
static readonly string fileName = "CommonResourceExample.txt";
public string ReplaceFileContents(string fileContent)
{
try
{
mutex.WaitOne();
string result = ReadFileCore();
WriteFileCore(fileContent);
return result;
}
finally
{
mutex.ReleaseMutex();
}
}
void WriteFileCore(string fileContent)
{
using (IsolatedStorageFile isolatedStorageFile
= IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream outStream
= isolatedStorageFile.OpenFile(
fileName, FileMode.Create))
{
using (StreamWriter writer = new StreamWriter(outStream))
{
writer.Write(fileContent);
}
}
}
}
string ReadFileCore()
{
using (IsolatedStorageFile isolatedStorageFile
= IsolatedStorageFile.GetUserStoreForApplication())
{
if (!isolatedStorageFile.FileExists(fileName))
{
return string.Empty;
}
using (IsolatedStorageFileStream inStream
= isolatedStorageFile.OpenFile(
fileName, FileMode.Open))
{
using (StreamReader reader = new StreamReader(inStream))
{
return reader.ReadToEnd();
}
}
}
}
}
It is important to ensure that your Mutex
is released upon exiting from a critical section. This is done by wrapping the section in a try/finally block. If your foreground app or your background agent terminates while owning a mutex, the state of the mutex is set to signaled and the next waiting thread gets ownership.
To test whether the Mutex
is owned, you can specify a timeout parameter to the WaitOne
method (see Listing 32.12). This is an effective way to prevent your task agent from being terminated because it is blocked on a WaitOne
call. Recall that a PeriodicTask
has a maximum 15 seconds of execution time.
bool mutexAcquired = false;
try
{
mutexAcquired = mutex.WaitOne(10000);
if (mutexAcquired)
{
/* Access common resource here. */
Debug.WriteLine("mutex entered.");
}
}
finally
{
if (mutexAcquired)
{
mutex.ReleaseMutex();
}
}
if (!mutexAcquired)
{
/* Unable to acquire ownership of mutex. */
Debug.WriteLine("mutex timed out.");
}
Tip
To test whether a Mutex
is owned, without blocking the calling thread, use a timeout value of 0 when calling WaitOne
.
Mutex
provides an effective way to synchronize critical sections of code and can assist in avoiding race conditions and other concurrency issues when accessing shared resources.
18.118.198.138