ReaderWriterLock not working in the ServiceBehavior constructor

I have a WCF service where InstanceContextMode- Single, and ConcurrencyMode- Multiple. The goal is to create a value cache when instantiating, not supporting other service calls that are not dependent on creating a cache.

Thus, only methods that try to get a read lock on _classificationsCacheLockwill have to wait until the value classificationsCache( classificationsCacheLock.IsWriterLockHeld = false) is filled .

However, the problem is that, despite acquiring a Lock record in the task flow, the WCF call continues to serve in response to the service method call GetFOIRequestClassificationsList(), the result _classificationsCacheLock.IsWriterLockHeldis equal falseto when this should be true.

Is this weird behavior with WCFinstancing or is it basically I don’t miss the trick.

I tried both to obtain a write lock in the context of the constructor thread (safe option) and in the context of the generated task (which can lead to a run between WCFand then call a function call GetFOIRequestClassificationsList()faster than a call classificationsCacheLock.AcquireWriterLock(Timeout.Infinite);), but both results lead to classificationsCacheLock.IsWriterLockHeld false, despite the fact that they prevented any race condition with thread.sleep, appropriately distributed in the code blocks of each corresponding thread.

[ServiceBehavior(Namespace = Namespaces.MyNamespace,
    ConcurrencyMode = ConcurrencyMode.Multiple,
InstanceContextMode = InstanceContextMode.Single)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService : IMyService
{
    private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

    private List<string> _classificationsCache;

    private ReaderWriterLock _classificationsCacheLock;

    public MyService()
    {
        try
        {
            _classificationsCacheLock = new ReaderWriterLock();
            LoadCache();
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
        }

    }

    private void LoadCache()
    {
        // _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite); 

        Task.Factory.StartNew(() =>
        {
            try
            {
                _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite); // can only set writer on or off on same thread, not between threads
                if (_classificationsCache == null)
                {
                    var cases = SomeServices.GetAllFOIRequests();
                    _classificationsCache = cases.SelectMany(c => c.Classifications.Classification.Select(cl => cl.Group)).Distinct().ToList();
                }

            }
            catch (Exception ex)
            {

                _logger.Error(ex);

            }
            finally
            {

                if (_classificationsCacheLock.IsWriterLockHeld)
                    _classificationsCacheLock.ReleaseWriterLock();
            }

        });//.ContinueWith((prevTask) =>
        //{
        //     if (_classificationsCacheLock.IsWriterLockHeld)
        //          _classificationsCacheLock.ReleaseWriterLock();
        //  });

    }

     public GetFOIRequestClassificationsList_Response GetFOIRequestClassificationsList()
    {
        try
        {

            GetFOIRequestClassificationsList_Response response = new GetFOIRequestClassificationsList_Response();

            _classificationsCacheLock.AcquireReaderLock(Timeout.Infinite);
            response.Classifications = _classificationsCache;
            _classificationsCacheLock.ReleaseReaderLock();

            return response;

        }
        catch (Exception ex)
        {
            _logger.Error(ex);

            if (ex is FaultException)
            {
                throw;
            }
            else
                throw new FaultException(ex.Message);
        }
    }
}

EDIT 1

Since a number of suggestions were related to the uncertainty in the thread pool and how the task could handle thread affinity, I changed the method to explicitly create a new thread

var newThread = new Thread(new ThreadStart(() =>
        {
            try
            {
                Thread.Sleep(2000);

                Debug.WriteLine(string.Format("LoadCache - _classificationsCacheLock.GetHashCode - {0}", _classificationsCacheLock.GetHashCode()));
                Debug.WriteLine(string.Format("LoadCache - Thread.CurrentThread.ManagedThreadId-  {0} ", Thread.CurrentThread.ManagedThreadId));


                _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite); // can only set writer on or off on same thread, not between threads
                if (_classificationsCache == null)
                {
                    var cases = SomeServices.GetAllFOIRequests();
                    _classificationsCache = cases.SelectMany(c => c.Classifications.Classification.Select(cl => cl.Group)).Distinct().ToList();
                }

            }
            catch (Exception ex)
            {

                _logger.Error(ex);

            }
            finally
            {

                if (_classificationsCacheLock.IsWriterLockHeld)
                    _classificationsCacheLock.ReleaseWriterLock();
            }

        }));
        newThread.IsBackground = true;
        newThread.Name = "MyNewThread" 
        newThread.Start();

. classificationsCacheLock.AcquireReaderLock /, .

, :

  • , , R/W
  • _classificationsCacheLock

    public GetFOIRequestClassificationsList_Response GetFOIRequestClassificationsList()   {              {

            GetFOIRequestClassificationsList_Response response = new GetFOIRequestClassificationsList_Response();
    
            Debug.WriteLine(string.Format("GetFOIRequestClassificationsList - _classificationsCacheLock.GetHashCode - {0}", _classificationsCacheLock.GetHashCode()));
            Debug.WriteLine(string.Format("GetFOIRequestClassificationsList - Thread.CurrentThread.ManagedThreadId - {0} ", Thread.CurrentThread.ManagedThreadId));
    
            Thread.Sleep(1000);
    
             _classificationsCacheLock.AcquireReaderLock(Timeout.Infinite);
            //_classificationsCacheMRE.WaitOne();
    
            response.Classifications = _classificationsCache;
    
            _classificationsCacheLock.ReleaseReaderLock();
    
            return response;
    
        }
        catch (Exception ex)
        {
            _logger.Error(ex);
    
            if (ex is FaultException)
            {
                throw;
            }
            else
                throw new FaultException(ex.Message);
        }
    }
    

..

GetFOIRequestClassificationsList - _classificationsCacheLock.GetHashCode - 16265870
GetFOIRequestClassificationsList - Thread.CurrentThread.ManagedThreadId - 9 
LoadCache - _classificationsCacheLock.GetHashCode - 16265870
LoadCache - Thread.CurrentThread.ManagedThreadId-  10  

.. , , , . WCF , .

  _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite);

, , - .

AcquireWriterLock , , WCF WCF, .

 private void LoadCache()
    {

        _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite);

        Debug.WriteLine(string.Format("LoadCache constructor thread - _classificationsCacheLock.GetHashCode - {0}", _classificationsCacheLock.GetHashCode()));
        Debug.WriteLine(string.Format("LoadCache constructor thread - Thread.CurrentThread.ManagedThreadId-  {0} ", Thread.CurrentThread.ManagedThreadId));

        var newThread = new Thread(new ThreadStart(() =>
        {
            try
            {
                Thread.Sleep(5000);

                Debug.WriteLine(string.Format("LoadCache new thread - _classificationsCacheLock.GetHashCode - {0}", _classificationsCacheLock.GetHashCode()));
                Debug.WriteLine(string.Format("LoadCache new thread - Thread.CurrentThread.ManagedThreadId-  {0} ", Thread.CurrentThread.ManagedThreadId));


              //  _classificationsCacheLock.AcquireWriterLock(Timeout.Infinite); // can only set writer on or off on same thread, not between threads
                if (_classificationsCache == null)
                {
                    var cases = SomeServices.GetAllFOIRequests();
                    _classificationsCache = cases.SelectMany(c => c.Classifications.Classification.Select(cl => cl.Group)).Distinct().ToList();
                }

            }
            catch (Exception ex)
            {

                _logger.Error(ex);

            }
            finally
            {

                if (_classificationsCacheLock.IsWriterLockHeld)
                    _classificationsCacheLock.ReleaseWriterLock();
            }

        }));
        newThread.IsBackground = true;
        newThread.Name = "CheckQueues" + DateTime.Now.Ticks.ToString();
        newThread.Start();
}

AcquireWriterLock Cache.

..

LoadCache constructor thread - _classificationsCacheLock.GetHashCode - 22863715
LoadCache constructor thread - Thread.CurrentThread.ManagedThreadId-  9 
GetFOIRequestClassificationsList - _classificationsCacheLock.GetHashCode - 22863715
GetFOIRequestClassificationsList - Thread.CurrentThread.ManagedThreadId - 8 
LoadCache new thread - _classificationsCacheLock.GetHashCode - 22863715
LoadCache new thread - Thread.CurrentThread.ManagedThreadId-  10 

2

.

, .

reset .

MRE , ReaderWriterLock , .

.net 4.0 - #

+4
1

CaseWork() ReaderWriterLock . , Garbage COllector, . , .

static , static?

, , , . , Lazy<T>. - , . TPL :

private Lazy<List<string>> _classificationsCache = new Lazy<List<string>>
(() => 
{
    var cases = SomeServices.GetAllFOIRequests();
    return cases.SelectMany(c => c.Classifications.Classification.Select(cl => cl.Group)).Distinct().ToList();
});

, :

response.Classifications = _classificationsCache.Value;

MSDN:

, . . . , AcquireWriterLock, ReleaseWriterLock .

, :

( Task.StartNew TaskScheduler.Current) , , priveleges, Task, . .

+1

All Articles