-1

Say I have two threads I want to be able to access an object, but not if a third thread is using it (and vice versa). What I can do is use 2 locks:

Thread A:

lock(object1)
{
   // do work
}

Thread B:

lock(object2)
{
   // do work
}

Thread C:

lock(object1)
{
    lock(object2)
    {
        // do work
    }
}

Is there a better way, or is this the correct way to do it?

Maverick Meerkat
  • 5,737
  • 3
  • 47
  • 66
  • Why is it okay for A and B to access the object at the same time? – Servy Oct 03 '17 at 17:09
  • @Servy - in my case they both insert rows into a SQLite DB, and it seems the DB can handle it... – Maverick Meerkat Oct 03 '17 at 17:11
  • Then it sounds like you should be doing your locking at a DB level, not at an application level. If you have a query that can't support other queries on the table while it's working, it sounds like you need to take out at table lock (or some other appropriate DB level locking strategy). If you only use application locking then the code will break as soon as more than one application is using the database. – Servy Oct 03 '17 at 17:13
  • relational databases have their own (usually numerous and sophisticated) locking mechanisms to deal with concurrency and transactional integrity. what are you really trying to achieve here? if it is *one* object, what is `object2`? – Cee McSharpface Oct 03 '17 at 17:13
  • @Servy - I (in Thread C) need to do some syncing between DB and real world - and in some cases remove from DB. Since the calculations are done in the app, I can't allow new inserts into the DB while the removal takes place. Otherwise I might remove new stuff that shouldn't get removed and will incur data loss. – Maverick Meerkat Oct 03 '17 at 17:16
  • @DavidRefaeli So you need a table level lock then, and locking in the application *won't work* because if you use locks in your application code then another application would be able to insert items into the table while you're doing your work. – Servy Oct 03 '17 at 17:20
  • @Servy no, since it's only being used by my app, and my app has one instance :-) – Maverick Meerkat Oct 03 '17 at 17:21
  • @Servy are there no other scenarios where you would want to allow 2+ threads to access resource/do work, but not when another thread does it ? – Maverick Meerkat Oct 03 '17 at 17:22
  • @DavidRefaeli Until the day that there's a second, and then a third, and so on. Databases have locks for exactly this reason. Use them, so that your code reliably works, rather than breaking as soon as you start to scale up your product because you intentionally chose to not use the right tool for the job. – Servy Oct 03 '17 at 17:23
  • @DavidRefaeli There are lots of times where that's something that you want to do. This isn't one of those times. This is an occasion where you have a resource that has a larger scope than just your application, and so it needs to use a locking mechanism that has the same scope, so that *every* consumer of that resource goes through the lock. When you only have *some* of the people consuming a resource that cannot be shared, that resource ends up being shared when it isn't supposed to be. – Servy Oct 03 '17 at 17:24
  • "I can't allow new inserts [...] while the removal takes place": inserts and removal, from a database point of view, are both data modifying commands - so wrap the data access operations in each thread in a serializable [transaction](https://sqlite.org/lang_transaction.html). then the RDBMS takes care of all the rest. – Cee McSharpface Oct 03 '17 at 17:26
  • @dlatikay I don't want serialized transaction - I want to be able to insert fast. – Maverick Meerkat Oct 03 '17 at 17:28
  • @Servy so how do I lock/unlock a SQLite DB, from code (I'm using Entity)? – Maverick Meerkat Oct 03 '17 at 17:28
  • @DavidRefaeli What does google say when you searched for how to do that? – Servy Oct 03 '17 at 17:29
  • @Servy it actually says the Entity doesn't support locking (Pessimistic Concurrency). So we're back in square 1. And you can answer my question again. Lucky you – Maverick Meerkat Oct 03 '17 at 17:49
  • It is SQLite. Emphasis on lite. You are playing a dangerous game, [read this](https://sqlite.org/threadsafe.html). – Hans Passant Oct 03 '17 at 18:06
  • Leaving aside the points abundantly made that this is probably the wrong thing to do entirely -- is it possible that thread A ever *also* takes a lock on object2 *while* it has a lock on object1? (Either because the lock was taken in the caller, or if the body of the lock calls something which takes the lock.) Similarly is it possible for thread B to hold both locks at the same time? If it is, then you almost certainly have written code that can deadlock. – Eric Lippert Oct 03 '17 at 21:05
  • I'm completely amazed as to the amount of stuck up comments and responses I get here from people who are not familiar with my code, and which are completely un-helpful. My question was general, in the sense of trying to understand what is the right way to achieve a certain goal. Instead of answering it, you decide to question the necessity of it. – Maverick Meerkat Oct 05 '17 at 11:28
  • But even specifically - SQLite is used as an in-app DB, it is only being used by the running app. No other app, and no scalability issues exist. There is also no way of getting a dead-lock, thank you very much. And also since I'm using entity - no way for using locks. So I think I proved the point of the necessity of my original general question. How about you guys actually try to answer it? Or at-least try to help and say "that's how you should do it", instead of just down-voting and voting to close a question? Is this site a piss contest or actually a forum to share knowledge??? – Maverick Meerkat Oct 05 '17 at 11:31

1 Answers1

1

One possibility is to use a ReaderWriterLockSlim. If you consider threads A and B to be readers, then they can read concurrently if thread C is not using it. And thread C can acquire the write lock to wait for A and B to exit, and to prevent them from getting the lock when C is running.

Example:

At outer scope:

ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();

Threads A and B:

rwLock.EnterReadLock();
try
{
    // do stuff
}
finally
{
    rwLock.ExitReadLock();
}

Thread C:

rwLock.EnterWriteLock();
try
{
    // do stuff
}
finally
{
    rwLock.ExitWriteLock();
}

You might also be interested in ReaderWriterLock

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Given what the OP has said in the comments about why they're doing what they're doing, this wouldn't solve their problems. – Servy Oct 03 '17 at 17:21
  • @Servy: In the limited scope of the OP's question and comments, this will in fact work. I agree, however, that it won't work if he gets more than one application accessing the database. Depending on the DB-level locking is a much better long-term solution. – Jim Mischel Oct 03 '17 at 17:25
  • this is actually a nice way to do what I want to do. Thanks – Maverick Meerkat Oct 05 '17 at 11:34