Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lock statement does not appear to be working

I have this method:

public bool Remove(EntityKeyType key)
{
    lock (syncroot)
    {
        //wait if we need to
        waitForContextMRE.Wait();

        //if the item is not local, assume it is not remote.
        if (!localCache.ContainsKey(key)) return false;

        //build an expression tree
        Expression<Func<EntityType, bool>> keyComparitorExpression = GenerateKeyComparitorExpression(key);

        var itemToDelete = TableProperty.Single(keyComparitorExpression);

        //delete from db
        TableProperty.DeleteOnSubmit(itemToDelete);
        DataContext.SubmitChanges();

        //get the removed item for OnCollectionChanged
        EntityType itemToRemove = localCache[key];
        itemToRemove.PropertyChanged -= item_PropertyChanged;

        //remove from the list
        Debug.Assert(localCache.Remove(key));

        //call the notification
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemToRemove));
        return true;
    }
}

I am calling it from multiple threads (calling the same instance), but an exception keeps being thrown on TableProperty.Single (Sequence contains no elements). Upon debugging the code I saw that a situation is being created where the item is being deleted from the database after a different thread has checked the cache for its existence. This should not be possible unless there are multiple threads inside the lock statement (The syncroot object is definitely the same instance across threads).

Impossible? I have proof: Impossible situation

There are three threads inside the lock statement! What gives?

notes:

  1. The MRE is set (not blocking).
  2. This is not a situation where the exception gets thrown, it just shows multiple threads inside a lock section. Update: I changed the image to an intellitrace event of the exception. The old image is here
  3. The syncroot object is not static, because I only want calls to the same instance syncronized.

Update

This is the declaration of the syncroot object:

private object syncroot = new object();

And some other declarations:

private ManualResetEventSlim waitForContextMRE = new ManualResetEventSlim(true);
private DataContextType _dataContext;
private System.Data.Linq.Table<EntityType> _tableProperty;
//DataContextType and EntityType are generic type parameters

I cannot make the syncroot static because I have several instances of the class running and it is important that they don't block each other. But that doesn't really matter - making it static does not fix the problem.

The ManualResetEvent (waitForContextMRE) is not there for synchronization - it's there to block database operations for a certain time after certain operations are performed (i.e. on startup). It is set most of the time. Taking it out of the lock block also does not fix the problem.

like image 578
Cillié Malan Avatar asked Dec 25 '11 21:12

Cillié Malan


People also ask

What is lock() in c#?

The lock statement acquires the mutual-exclusion lock for a given object, executes a statement block, and then releases the lock. While a lock is held, the thread that holds the lock can again acquire and release the lock. Any other thread is blocked from acquiring the lock and waits until the lock is released.

Does lock block thread c#?

Locks will block other threads from executing the code contained in the lock block. The threads will have to wait until the thread inside the lock block has completed and the lock is released.

How to lock threads in c#?

The lock keyword is used to get a lock for a single thread. A lock prevents several threads from accessing a resource simultaneously. Typically, you want threads to run concurrently. Using the lock in C#, we can prevent one thread from changing our code while another does so.


2 Answers

The only explanation I have is that waitForContextMRE.Wait(); calling this makes the thread do unblock syncroot! And so other thread can enter lock section. Try to move waitForContextMRE.Wait(); before lock(...).

like image 115
Elastep Avatar answered Sep 20 '22 02:09

Elastep


I think that you are calling different objects. There is no indication on your screenshot that you are taking values from different threads. Also using non static syncroot is not a good idea because may result in cases like yours. Do you have really strong reason not to have it static?

like image 39
the_joric Avatar answered Sep 21 '22 02:09

the_joric