Introduction
Some applications need to share access to a restricted resource in a
coordinated manner on one PC, like a dodgy print driver to which only one application can print
at a time. All preemptively multitasking operating systems like Windows and Unix provide a
mechanism called semaphores with which several running applications can coordinate access to restricted
resources. In my case, I want to make sure that only one application at a time
is trying to print a document to my dodgy print driver.
Explanation
Semaphores are named objects in the operating system. If several applications ask
the operating system for a semaphore with the same name, then each
application will be given a link to the same semaphore in the operating system. Name your
semaphores something which you find meaningful and that all application sharing the resource must know.
In my code sample I have named the semaphore "sem_DotNetExample" but you should name
your semaphore after the resource it is being used to coordinate access to,
like "sem_DodgyPrintDriver". Each semaphore keeps a count of how many applications can concurrently
use the resource. In my case, I only want one application to
use the resource at a time and other applications requesting to use the resource should wait
until the resource becomes available. When the semaphore is created the operating system is
told how high the count for the semaphore is and each time an application uses the semaphore,
the count is decremented. If the count is zero when an application tries to use the
semaphore, the application goes into an efficient waiting state, consuming very little CPU time
until another application releases the resource. When your application has finished using the resource,
it releases the semaphore, incrementing the availability count of the semaphore. Finally
when your program shuts down it should clean up and delete its reference to the semaphore.
The Windows operating system provides many functions for working with semaphores, all of
which are in the kernel32 library. The source code provides maps to these kernel functions and
then makes the calls to the operating system when the application's buttons
are clicked by a user. The way to map these functions in the kernel to C# methods is
as follows. A working encapsulation of the semaphore functionality is included in the Semaphore class available
in the source code download should you wish to include this functionality in your own projects.
[DllImport("kernel32", EntryPoint="CreateSemaphore",
SetLastError=true,CharSet=CharSet.Unicode)]
private static extern uint NT_CreateSemaphore(
SecurityAttributes auth,
int initialCount,
int maximumCount,
string name);
[DllImport("kernel32",EntryPoint="WaitForSingleObject",
SetLastError=true,CharSet=CharSet.Unicode)]
private static extern uint NT_WaitForSingleObject(
uint hHandle,
uint dwMilliseconds);
[DllImport("kernel32",EntryPoint="ReleaseSemaphore",
SetLastError=true,CharSet=CharSet.Unicode)]
[return : MarshalAs( UnmanagedType.VariantBool )]
private static extern bool NT_ReleaseSemaphore(
uint hHandle,
int lReleaseCount,
out int lpPreviousCount);
[DllImport("kernel32",EntryPoint="CloseHandle",
SetLastError=true,CharSet=CharSet.Unicode)]
[return : MarshalAs( UnmanagedType.VariantBool )]
private static extern bool NT_CloseHandle(uint hHandle);
[DllImport("kernel32",EntryPoint="GetLastError",SetLastError=true)]
private static extern uint NT_GetLastError();
To see the application in action, download the demo project
and run several instances of the application, say four. Click the "Create" button in
each instance of the application to get a handle, or reference, to the semaphore. In each application
then click the "Take" button to instruct each application that it should request access to
the resource. Only the first application in which you clicked the "Take" button returns
immediately with the message "Took the semaphore successfully." and the other instances all
go into a waiting state. Now click the "Give" button in the application which owns the
semaphore and the next application in the queue will immediately return from its waiting state with the
message "Took the semaphore successfully." and so on in turn clicking the "Give" button in
each application until each application has had ownership of the semaphore. When you're done, tell the
operating system that you're done with the semaphore by closing the handle to it, click the "Destroy" button.
Conclusion
Share your resources well. :-)
(This article is dedicated to His Holiness, the 17th Gyalwa Karmapa, Trinley Thaye Dorje)
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.