Upload
reynard-goodman
View
214
Download
2
Embed Size (px)
Citation preview
Lecture 6:
Monitors & Semaphores
Monitor
Contains data and procedures needed to allocate shared resources
Accessible only within the monitor
No way for threads outside monitor to access monitor data
Resource allocation using Monitors
Thread must call monitor entry routine
Mutual exclusion is rigidly enforced at monitor boundary
A thread that tries to enter monitor when it is in use must wait
Monitor
Operating Systems - Deitel & Associates - 2004
lock( ) vs. monitor( )
Both lock( ) and monitor( ) are methods of enforcing mutual exclusion in modern programming languages such as C# and Java. In the .NET environment, lock( ) is preferred since it manages the thread queue.
Like the lock keyword, monitors prevent blocks of code from simultaneous execution by multiple threads. The Enter method allows one and only one thread to proceed; all other threads are blocked until the executing thread calls Exit. This is just like using the lock keyword. In fact, the lock keyword is implemented with the Monitor class.
Using the lock is generally preferred over using the Monitor class directly, both because lock is more concise, and because lock ensures that the underlying monitor is released, even if the protected code throws an exception. This is accomplished with the finally keyword, which executes its associated code block regardless of whether an exception is thrown.
http://msdn.microsoft.com/en-us/library/ms173179%28VS.80%29.aspx
using System;using System.Threading;namespace monitor_demo_02{ public class Test { static int count = 0; static void Main() { ThreadStart job = new ThreadStart(ThreadJob); Thread thread = new Thread(job); thread.Start(); for (int i = 0; i < 5; i++) { int tmp = count; Console.WriteLine("Read count={0}", tmp); Thread.Sleep(50); tmp++; Console.WriteLine("Incremented tmp to {0}", tmp); Thread.Sleep(20); count = tmp; Console.WriteLine("Written count={0}", tmp); Thread.Sleep(30); }
thread.Join(); Console.WriteLine("Final count: {0}", count); Console.ReadKey(); }
Multi-Threaded Demono Monitor
http://www.yoda.arachsys.com/csharp/multithreading.html
static void ThreadJob() { for (int i = 0; i < 5; i++) { int tmp = count; Console.WriteLine("\t\t\t\tRead count={0}", tmp); Thread.Sleep(20); tmp++; Console.WriteLine("\t\t\t\tIncremented tmp to {0}", tmp); Thread.Sleep(10); count = tmp; Console.WriteLine("\t\t\t\tWritten count={0}", tmp); Thread.Sleep(40); } } }}
Multi-Threaded Demo - concluded
Read count=0 Read count=0 Incremented tmp to 1 Written count=1Incremented tmp to 1Written count=1 Read count=1 Incremented tmp to 2Read count=1 Written count=2 Read count=2Incremented tmp to 2 Incremented tmp to 3Written count=2 Written count=3Read count=3 Read count=3 Incremented tmp to 4 Written count=4Incremented tmp to 4Written count=4 Read count=4Read count=4 Incremented tmp to 5 Written count=5Incremented tmp to 5Written count=5Read count=5Incremented tmp to 6Written count=6Final count: 6
Multi-Threaded Sample Output
using System;using System.Threading;
namespace monitor_demo_01{ public class Test { static int count = 0; static readonly object countLock = new object();
static void Main() { ThreadStart job = new ThreadStart(ThreadJob); Thread thread = new Thread(job); thread.Start();
for (int i = 0; i < 5; i++) { Monitor.Enter(countLock); int tmp = count; Console.WriteLine("Read count={0}", tmp); Thread.Sleep(50); tmp++; Console.WriteLine("Incremented tmp to {0}", tmp); Thread.Sleep(20); count = tmp; Console.WriteLine("Written count={0}", tmp); Monitor.Exit(countLock); Thread.Sleep(30); } thread.Join(); Console.WriteLine("Final count: {0}", count); Console.ReadKey(); }
Monitor Demo
static void ThreadJob() { for (int i = 0; i < 5; i++) { Monitor.Enter(countLock); int tmp = count; Console.WriteLine("\t\t\t\tRead count={0}", tmp); Thread.Sleep(20); tmp++; Console.WriteLine("\t\t\t\tIncremented tmp to {0}", tmp); Thread.Sleep(10); count = tmp; Console.WriteLine("\t\t\t\tWritten count={0}", tmp); Monitor.Exit(countLock); Thread.Sleep(40); } } }}
Monitor Demo - concluded
Read count=0Incremented tmp to 1Written count=1 Read count=1 Incremented tmp to 2 Written count=2Read count=2Incremented tmp to 3Written count=3 Read count=3 Incremented tmp to 4 Written count=4Read count=4Incremented tmp to 5Written count=5 Read count=5 Incremented tmp to 6 Written count=6Read count=6Incremented tmp to 7Written count=7 Read count=7 Incremented tmp to 8 Written count=8Read count=8Incremented tmp to 9Written count=9 Read count=9 Incremented tmp to 10 Written count=10Final count: 10
Monitor Demo Sample Output
Semaphores
Software construct that can be used to enforce mutual exclusion
Contains a protected variable
Can be accessed only via wait and signal commands
Also called P and V operations, respectively
Semaphores
Binary semaphore: allow only one thread in its critical section at any time...
Wait operation
If no threads are waiting, allow thread into its critical section
Decrement protected variable (to 0 in this case)
Otherwise place in waiting queue
Signal operation
Indicate that thread is outside its critical section
Increment protected variable (from 0 to 1)
A waiting thread (if there is one) may now enter
Binary Semaphore
Counting semaphores
Initialized with values greater than one
Can be used to control access to a pool of identical resources
Decrement the semaphore’s counter when taking resource from pool
Increment the semaphore’s counter when returning it to pool
If no resources are available, thread is blocked until a resource becomes available
Counting Semaphores
Semaphores
A semaphore is a protected variable whose value can be accessed and altered only by the operations P( ) and V( ) and an initialization operation SemaphoreInit( ). Binary semaphores can have values true or false while counting semaphores can have non-negative integer values.
function P(S:semaphore) if S>0 then S:=S-1; else wait_for(S); end if;end P;
function V(S:semaphore) if waiting_for(S) then release_for(S); else S:=S+1; end if;end V;
wait_for(S) adds the process calling P(S) to a queue.
waiting_for(S) is a boolean function that checks to see if the queue is empty.
release_for(S) releases the process waiting at the front of the queue.
Using Semaphores: Mutual Exclusion
program example_one is active : semaphore;
procedure P1 isbegin while true do begin stuff; P(active); critical_region; V(active); end;end P1;
procedure P2 isbegin while true do begin stuff; P(active); critical_region; V(active); end;end P2;
begin example_one semaphore_init(active,1); parbegin P1; P2; parend;end example_one;
The semaphore active is set to one (1) and acts to control the process’s access to its critical section.
Using Semaphores: Producer-Consumer
Interprocess communication occurs when one process passes data to another process. This is more complex than a procedure call with parameter passing since the two processes do not necessarily share the same address space. The processes must be synchronized which means that the data transfer must occur in the proper time sequence.
procedure producer is nextvalue : integer;begin while true do begin calculate(nextvalue); P(access_numbuffer); numbuffer:=nextvalue; V(access_numbuffer); V(numbuffer_loaded); end;end producer;
procedure consumer is nextvalue : integer;begin while true do begin P(numbuffer_loaded); P(access_numbuffer); nextvalue:=numbuffer; V(access_numbuffer); make_use_of(nextvalue); end;end consumer;
Mutual Exclusion with Test-and-Set
When the computer hardware can be used to support the enforcement of mutual exclusion, the complexity of the resulting software is greatly reduced. The indivisible instruction testandset(a,b)reads the value of a boolean b, copies it into a and then sets b to true all within the span of a single uninterruptible instruction.
procedure P1 no_P1 : boolean;begin while true do begin no_P1:=true; while no_P1 do testandset(no_P1,go); critical_region; go:=false; other_stuff; end;end P1;
procedure P2 no_P2 : boolean;begin while true do begin no_P2:=true; while no_P2 do testandset(no_P2,go); critical_region; go:=false; other_stuff; end;end P2;
Summary
Monitors hold data to be protected from concurrent asynchrononous access
Monitors can be used to manage shared resources of any type
In C# lock( ) is preferred when enforcing mutual exclusion of shared data
Semaphores require special hardware implementation (binary and counting)
Test-and-Set is an uninteruptable instruction