Monday, November 15, 2010

Concurrency -ConcurrencyMode

Concurrency:

 

This issue arrises when multiple request attempts to access the same Service object at runtime. i.e. (PerSession / Singleton Instancing Mode).

For PerCall there will be no concurrency issue, as a new Service object is instantiated for every request.

However PerSession/Singleton Services are at concurrency risk candidates.

 

Three Modes:

 

·         ConcurrencyMode.Single (Default) – A single thread has access to a Service Object at a given time.

·         ConcurrencyMode.Reentrant –

·         A single thread has access to a Service Object at a given time.

·         But the thread can exit the Service and can reenter without deadlock.

 

·         ConcurrencyMode.Multiple –

·         Multiple request threads have access to the service object.

·         Shared resources must be manually protected from concurrent access.

 

ConcurrencyMode.Single with PerSession/Singleton

 

·         In this case, a lock is acquired for the Service Object, while a request is being processed by that object.

·         Other calls to the same Service Object are blocked & queued.

·         When the request having the lock has completed, the lock is released. Then the next request in the queue acquires the lock and starts processing.

 

Fig:1 –Single - PerCall

Fig:2 – Single - PerSession

Fig:3 – Single - Singleton

 

 

 

ConcurrencyMode.Reentrant

 

Reentrant mode behaves similar to Single Mode. i.e. does not allows concurrent calls from clients.

However, Renentrant mode is required when a Service issues Callbacks (Two-Way). Single mode cannot handle these callbacks. (Fig 4)

 

 

 

 

 

Reentrant -PerCall

Fig 5

·         With Reentrant, when client callback is made (Label 2), the lock on the service instance is released so that the another call (Label 3), is allowed to acquire it. Fig 5.

·         When the outgoing call is returned (Fig5. - Label 3),it is queued to acquire the lock to complete its work.

 

This is not possible with Single-PerCall– In Single mode, deadlock is guaranteed, where as in Reentrant mode, the callback (Fig5. - Label 3) simply reacquires the lock.e.g. Compare Figure 4 & 5

 

Reentrant -PerSession

Fig  6

·         Reentrant – PerSession gives more thruput as compared 'Single – PerSession' (Fig 2).

·         Reason being while the callback is in progress, another call T2 (labelled 2) from the client can acquire the lock, as the first thread(T1) has released it issue a callback.

·         T1 queued up on return, waiting for T2 to complete the work.

 

Reentrant - Singleton

 

Fig 7

·         Reentrant – Singleton gives more thruput as compared 'Single – Singleton'(Fig 3).

·         Reason being while the callback is in progress, another call from any client (T2/T3/T4) can acquire the lock, as the first thread(T1) has released it issue a callback.

·         Here for e.g. T3 has acquired the lock as the first thread(T1) has released it issue a callback.

·         T1 then later queued up on return, waiting for T3 to complete the work

 

 

 

Fig 4 – Single – PerCall with Callback

Fig:5 –Reentrant – PerCall

Fig:6 – Reentrant - PerSession

Fig:7 – Reentrant - Singleton

cid:image010.png@01CB55D4.9DA22CB0

cid:image006.png@01CB55D2.F011E9B0

cid:image004.png@01CB55D9.328CE5F0

cid:image009.png@01CB55AD.532751A0

 

 

Reentrant – Can highly affect the 'Integrity'

 

·         Renetrancy can highly affect the state of object in case of PerSession & Singleton – Figure 6,7.

·         Reason being – A new thread (T2) will be using the same service object, while another thread T1 may have not compeleted it's work (busy with callback).

·         Thus make sure that the thread's state is always persisted safely with a resource manager, such as DB before releasing the lock for callback.This same persisted state could be retrieved  upon the callback's return, if applicable.

 

ConcurrencyMode.Multiple

 

Multiple mode provides high thruput, as the multiple threads are allowed to access to the same/shared Service object.

In this case, no locks are acquired on the Service object. All Shared State & Resources must be protected with Manual Synchronization Techniques.

 

In case of PerSession & Singleton, the 'State' is shared in the Service Instance.

Now here there could be two possiblilites.

 

·         If the State, itself references threadsafe objects, that encapsulate their own concurrency protection, then the first thread (T1) to access the State obejct, will acquire the lock for that State object (Fig 9,10). The other threads (T2 will queue, if it tries to access the State object.

·         If not, Service object should implement sycnhronization locks, before accessing those resources.

 

 

Fig:8 – Multiple - PerCall

Fig:9 – Multiple - PerSession

Fig:10 – Multiple - Singleton

cid:image012.png@01CB569B.17F394B0

cid:image015.png@01CB58F3.A1A95F00

cid:image016.png@01CB58F3.8D60A710

 

Note: In case of Multiple – No thread are blocked (See Green cricles). i.e this is the case where the maximum concurrency attention is required.

 

 

 

 

Multiple -PerCall

Fig 8

PerCall, are not immune to synchronization issues. Reason being, there is no possiblity of  'Shared State' in the service instance.

However, individual Service Instances, may access a 'Shared' resource. The shared resource could be:

 

·         Global Object – Relies on 'Manual Synchronization Techniques'

·         Shared Cache – Relies on 'Manual Synchronization Techniques'

·         Database – It relies on 'Resource Managers' & 'Transactions' to manage consistency & concurrency.

 

Here as shown in fig(8), manulaly locks have been applied on the Cache object. Thus once T1 acquires the lock, another thread T2 will wait. Thus avoiding Concurrency issue.

 

Multiple -PerSession

Fig  9

PerSession, could also have the 'PerCall' scenario (Shared Cache).

However, additionally, they are also likely to have 'Shared State' in the Service Instance.

 

Thus here one should implement the Manual/Custom Synchronziation to control the access to the 'Shared State' object.. The synchronization should

work in a way that if T1 has lock on that state object and if another thread(T2) tries access the state, then T2 should be queued.

 

Multiple - Singleton

 

Fig 10

Same as 'PerSession'.

 

Additionally, it is entirely possible, that multiple types of 'Shared State' exist in the same 'Service Instance' to increase the throughput.

In such cases, different locks should be acquired for relevant type of 'State' object.

This way, multiple thread T1& T3 can complete work concurrently, since they don't share state. T2 & T4 will queue, waiting for the respectives locks to free.

 

 

Manual/Custom Synchronization:

 

There are many ways to implement 'Custom' Syynchronization. Few are:

 

·         Monitor

·         Mutex

·         Semaphore

·         ReadWriterLock

·         Interlocked

·         .Net Attributes – Used to synchronize access to a type.

 

 

 

[ServiceContract(Namespace = "http://www.ArunM.com/samples/2006/06")]

    public interface IMessagingService

    {

        [OperationContract(IsOneWay=true)]

        void SendMessage1(string message);

 

        [OperationContract(IsOneWay=true)]

        void SendMessage2(string message);

   

        [OperationContract(IsOneWay=true)]

        void SendMessage3(string message);

    }

 

 

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single,

                 ConcurrencyMode=ConcurrencyMode.Multiple,

                                       UseSynchronizationContext=false)]

    public class MessagingService : IMessagingService

    {

        int m_messageCounter;

        Dictionary<int, string> m_messages = new Dictionary<int, string>();

        Mutex m_mutex = new Mutex(false);

 

        int m_messageCounter2;

        Dictionary<int, string> m_messages2 = new Dictionary<int, string>();

        Mutex m_mutex2 = new Mutex(false);

 

        #region IMessagingService Members

 

        public void SendMessage1(string message)

        {

            try

            {

                Monitor.Enter(this);

 

                m_messages.Add(m_messageCounter, message);

                Trace.WriteLine(string.Format("Message {0}: {1}", m_messageCounter, message));

                m_messageCounter++;

            }

            finally

            {

                Monitor.Exit(this);

            }

 

        }

 

        public void SendMessage2(string message)

        {

           lock(this)

           {

             m_messages.Add(m_messageCounter, message);

             Trace.WriteLine(string.Format("Message {0}: {1}", m_messageCounter, message));

             m_messageCounter++;

           }

        }

 

        [MethodImpl(MethodImplOptions.Synchronized)]

        public void SendMessage3(string message)

        {

           m_messages2.Add(m_messageCounter2, message);

           Trace.WriteLine(string.Format("Message {0}: {1}", m_messageCounter2, message));

           m_messageCounter2++;

        }

       

--
Posted By Arun Manglick to Technical on Satellite Site at 9/21/2010 08:04:00 AM




--
Arun Manglick
(Software Engineer)

No comments:

Post a Comment