When should I use Shared Locking (Read Locking) - multithreading

Can someone explain when shared locks should be used. If I understand correctly, shared locks are used when reading and exclusive locks are used while writing.
But why can't I just wait while a mutex is locked when doing a read.

It is for improving performance. Multiple concurrent reads then won't have to happen sequentially, which may be a great bonus if the structure is read frequently. (But still the data read will be consistent and up to date.)

But why can't I just wait while a mutex is locked when doing a read.
Usually for speed. Shared locks allow multiple readers, as the content is not changing. Exclusive lock allows only a single (typically) write operation as you want all the writes to be atomic.
More technical definitions from here.
Exclusive locks protect updates to file resources, both recoverable
and non-recoverable. They can be owned by only one transaction at a
time. Any transaction that requires an exclusive lock must wait if
another task currently owns an exclusive lock or a shared lock against
the requested resource. Shared locks
Shared locks support read integrity. They ensure that a record is not
in the process of being updated during a read-only request. Shared
locks can also be used to prevent updates of a record between the time
that a record is read and the next syncpoint.

A shared locks (also known as read locks) prohibits any other process from requesting a write lock on the specified part of the file. However, other processes can request read locks and keep reading from the resource.
An exclusive lock (also known as write lock) gives a process an exclusive access for writing to the specified part of the file and will prevent any other process to acquire any kind of locking to the resource until the exclusive lock is released.
So a read lock says "you can read now but if you want to write you'll have to wait" whereas a write lock says "you'll have to wait".
For an official description, check out the official locks documentation of GNU C library.
In addition, in many cases, the context of shared/exclusive locking is pessimistic/optimistic locking which is a methodology used to handle multi-users read/write to the same resource. Here is an explanation of the methodology.

But why can't I just wait while a mutex is locked when doing a read.
Because that would be inefficient in certain scenarios, specifically those where a lot of threads are reading a data structure often, but very few are writing and not very often. Since multiple concurrent reads are thread-safe if no one is writing, it would be a waste to have mutual exclusion for the readers.
Imagine a server and multiple clients doing various transactions on some shared data. If most of these clients are simply asking for information, but not changing anything, the server would have horrible performance if it only allowed one client to read at a time.

Related

What are the differences between pthread reader-writer locks and fcntl()-based file locks?

For a project I am thinking about using pthread reader-writer locks or fcntl()-based file locks. I have to choose on of them. Could you please explain the differences between them? What are the advantages and disadvantages?
They're two completely different tools and are generally used for different tasks. A fair or complete contrast between the two is difficult as it's like comparing apples to couches.
TL;DR:
fcntl(2):
Advisory locking interface
Primarily works on files
Easily works between multiple processes
pthread_rwlock:
Advisory locking interface
Serializes access to conflicting operations (writes/reads, writes/writes, reads/writes)
Provides safety for shared resources (memory, file descriptors, etc) between multiple threads in a single process
fcntl(2)-based locks implement POSIX advisory locks on files, or ranges of bytes within the files. As they are advisory, nothing enforces these locks -- all processes (or threads) must cooperate and respect the semantics of the locks for them to be effective. For example, consider two processes A and B operating on some file f. If process A sets locks on f, B can completely ignore these locks and do whatever it likes. In general, this interface is used to protect access to entire files (or ranges within a file) between multiple threads and / or processes.
The pthread_rwlock interface could also be considered an advisory locking system (all threads must use the API for the locking to be effective). However, it is not implemented on top of files and is not limited in scope to protecting access to files. A reader-writer lock is a form of shared memory mutual exclusion interface such that multiple readers may concurrently execute a critical section while writers are blocked, or such that individual writers may execute a critical section, blocking all other concurrent readers and writers. In general, this interface is used to safeguard access to shared mutable state (possibly shared memory, possibly file access) between multiple threads in a process in read-mostly workloads. This API is not typically used to protect concurrent access in multiple processes.
If I were faced with the decision on picking one of these interfaces for serializing access to some data, I'd expect to ask myself at least a couple of questions:
Am I primarily working on files?
Do I have multiple processes?
If the intent is largely to protect access to a file, but only ever in a single process, I might settle for using pthread_rwlock. The downside to this approach is that if I ever needed to use multiple processes to access the file in the future, I wouldn't have a good way to express my locking intent to those other processes. Similarly, if I'm primarily trying to serialize access to some shared memory, I would use pthread_rwlock because the fcntl(2) interface expresses some intent on a file.
When trying to cooperate between multiple processes reading and writing a single file, I might use fcntl(2). However, this is likely to become very complicated, very quickly. For example, how do I handle events like truncation? Consider a case where process A has read 1024 bytes into a file that is then truncated to 0 bytes by process B. A must then seek to the beginning of the file and wait for new data to be written to continue reading without errors -- or to correctly append new data itself!
Solving these issues requires more locked communication in additional files, and the complexity can quickly spiral out of control. If I was required to implement some sort of concurrent system working on a file, I'd likely choose multiple threads and use the pthread_rwlock API. It's just easier to manage the totality of updates required to implement such a system in a single process. Without knowing the requirements you're faced with, it's rather difficult to guide one way or another.

I'm not understanding what starvation means as this author explains it

In objc.io Issue 2 the author talks about the issue of Starvation with reading and writing:
Just when you thought that there are enough problems to think of, a new one comes around the corner. Locking shared resources can result in the readers-writers problem. In many cases, it would be wasteful to restrict reading access to a resource to one access at a time. Therefore, taking a reading lock is allowed as long as there is no writing lock on the resource. In this situation, a thread that is waiting to acquire a write lock can be starved by more read locks occurring in the meantime.
I assume a reading lock is only allowing the thing that locked it access to read the value, while all others are not able to access it? I understand that you could only read lock if there's no write lock (as the value might be changing), but in the way it uses therefore it seems to imply that "read locks are only allowed if there's no write locks" is used as a means of efficiency/not being wasteful. What does taking a reading lock have to do with being wasteful?
Also, could anyone explain what the last sentence means? I don't understand what impact the read locks would have, or what the read locks would even be on.
Basically what it means is that a read lock allows other items to read the data at the same time.
So imagine the following scenario.
A takes a read lock and starts reading.
Then X comes along and tries to take a write lock, but can't, because it's already locked.
Meanwhile, before A finishes reading, B comes along and takes a read lock. This is allowed because read locks can be concurrent.
Then, A finishes, but X still can't write, because it's locked by B.
Then, before B finishes, C comes along and takes out a read lock. Etc.
The problem occurs when you have "shared read" and "exclusive write" locks. Multiple "shared read" locks can be applied to the entity at the same time, and they will prevent any "exclusive write" lock from gaining access.
"Starvation" occurs when new "shared read" locks keep arriving, before all existing ones are released. This can hold off an "exclusive write" lock indefinitely.
There are several strategies for handling this, such as preventing any new "shared read" lock from being applied if there is an "exclusive write" lock waiting.
1) If only a single read lock were allowed on a shared resource, it would be "wasteful" because any threads which only wanted to read the shared resource would have to queue up and read the resource one at a time.
2) A read lock will not prevent other threads from taking out a read lock, but would prevent other threads from taking write locks.
3) As you can't write lock a shared resource which has one or more read locks on it, the starvation thing would occur if several threads where continually requesting read locks (imagine them in an overlapping fashion); the thread which wanted the write lock would never be able to take it as there would always be a read lock.

Real World Examples of read-write in concurrent software

I'm looking for real world examples of needing read and write access to the same value in concurrent systems.
In my opinion, many semaphores or locks are present because there's no known alternative (to the implementer,) but do you know of any patterns where mutexes seem to be a requirement?
In a way I'm asking for candidates for the standard set of HARD problems for concurrent software in the real world.
What kind of locks are used depends on how the data is being accessed by multiple threads. If you can fine tune the use case, you can sometimes eliminate the need for exclusive locks completely.
An exclusive lock is needed only if your use case requires that the shared data must be 100% exact all the time. This is the default that most developers start with because that's how we think about data normally.
However, if what you are using the data for can tolerate some "looseness", there are several techniques to share data between threads without the use of exclusive locks on every access.
For example, if you have a linked list of data and if your use of that linked list would not be upset by seeing the same node multiple times in a list traversal and would not be upset if it did not see an insert immediately after the insert (or similar artifacts), you can perform list inserts and deletes using atomic pointer exchange without the need for a full-stop mutex lock around the insert or delete operation.
Another example: if you have an array or list object that is mostly read from by threads and only occasionally updated by a master thread, you could implement lock-free updates by maintaining two copies of the list: one that is "live" that other threads can read from and another that is "offline" that you can write to in the privacy of your own thread. To perform an update, you copy the contents of the "live" list into the "offline" list, perform the update to the offline list, and then swap the offline list pointer into the live list pointer using an atomic pointer exchange. You will then need some mechanism to let the readers "drain" from the now offline list. In a garbage collected system, you can just release the reference to the offline list - when the last consumer is finished with it, it will be GC'd. In a non-GC system, you could use reference counting to keep track of how many readers are still using the list. For this example, having only one thread designated as the list updater would be ideal. If multiple updaters are needed, you will need to put a lock around the update operation, but only to serialize updaters - no lock and no performance impact on readers of the list.
All the lock-free resource sharing techniques I'm aware of require the use of atomic swaps (aka InterlockedExchange). This usually translates into a specific instruction in the CPU and/or a hardware bus lock (lock prefix on a read or write opcode in x86 assembler) for a very brief period of time. On multiproc systems, atomic swaps may force a cache invalidation on the other processors (this was the case on dual proc Pentium II) but I don't think this is as much of a problem on current multicore chips. Even with these performance caveats, lock-free runs much faster than taking a full-stop kernel event object. Just making a call into a kernel API function takes several hundred clock cycles (to switch to kernel mode).
Examples of real-world scenarios:
producer/consumer workflows. Web service receives http requests for data, places the request into an internal queue, worker thread pulls the work item from the queue and performs the work. The queue is read/write and has to be thread safe.
Data shared between threads with change of ownership. Thread 1 allocates an object, tosses it to thread 2 for processing, and never wants to see it again. Thread 2 is responsible for disposing the object. The memory management system (malloc/free) must be thread safe.
File system. This is almost always an OS service and already fully thread safe, but it's worth including in the list.
Reference counting. Releases the resource when the number of references drops to zero. The increment/decrement/test operations must be thread safe. These can usually be implemented using atomic primitives instead of full-stop kernal mutex locks.
Most real world, concurrent software, has some form of requirement for synchronization at some level. Often, better written software will take great pains to reduce the amount of locking required, but it is still required at some point.
For example, I often do simulations where we have some form of aggregation operation occurring. Typically, there are ways to prevent locking during the simulation phase itself (ie: use of thread local state data, etc), but the actual aggregation portion typically requires some form of lock at the end.
Luckily, this becomes a lock per thread, not per unit of work. In my case, this is significant, since I'm typically doing operations on hundreds of thousands or millions of units of work, but most of the time, it's occuring on systems with 4-16 PEs, which means I'm usually restricting to a similar number of units of execution. By using this type of mechanism, you're still locking, but you're locking between tens of elements instead of potentially millions.

Is a lock (threading) atomic?

This may sound like a stupid question, but if one locks a resource in a multi-threaded app, then the operation that happens on the resource, is that done atomically?
I.E.: can the processor be interrupted or can a context switch occur while that resource has a lock on it? If it does, then nothing else can access this resource until it's scheduled back in to finish off it's process. Sounds like an expensive operation.
The processor can very definitely still switch to another thread, yes. Indeed, in most modern computers there can be multiple threads running simultaneously anyway. The locking just makes sure that no other thread can acquire the same lock, so you can make sure that an operation on that resource is atomic in terms of that resource. Code using other resources can operate completely independently.
You should usually lock for short operations wherever possible. You can also choose the granularity of locks... for example, if you have two independent variables in a shared object, you could use two separate locks to protect access to those variables. That will potentially provide better concurrency - but at the same time, more locks means more complexity and more potential for deadlock. There's always a balancing act when it comes to concurrency.
You're exactly right. That's one reason why it's so important to lock for short period of time. However, this isn't as bad as it sounds because no other thread that's waiting on the lock will get scheduled until the thread holding the lock releases it.
Yes, a context switch can definitely occur.
This is exactly why when accessing a shared resource it is important to lock it from another thread as well. When thread A has the lock, thread B cannot access the code locked.
For example if two threads run the following code:
1. lock(l);
2. -- change shared resource S here --
3. unlock(l);
A context switch can occur after step 1, but the other thread cannot hold the lock at that time, and therefore, cannot change the shared resource. If access to the shared resource on one of the threads is done without a lock - bad things can happen!
Regarding the wastefulness, yes, it is a wasteful method. This is why there are methods that try to avoid locks altogether. These methods are called lock-free, and some of them are based on strong locking services such as CAS (Compare-And-Swap) or others.
No, it's not really expensive. There are typically only two possibilities:
1) The system has other things it can do: In this case, the system is still doing useful work with all available cores.
2) The system doesn't have anything else to do: In this case, the thread that holds the lock will be scheduled. A sane system won't leave a core unused while there's a ready-to-run thread that's not scheduled.
So, how can it be expensive? If there's nothing else for the system to do that doesn't require acquiring that lock (or not enough other things to occupy all cores) and the thread holding the lock is not ready-to-run. So that's the case you have to avoid, and the context switch or pre-empt issue doesn't matter (since the thread would be ready-to-run).

Do I need to lock object when reading from it?

I am writing a program where there is an object shared by multiple threads:
A) Multiple write threads write to the object (all running the same
function)
B) A read thread which accesses the object every 5 seconds
C) A read thread which accesses the object there is a user request
It is obviously necessary to lock the object when writing to it, as we do not want multiple threads to write to the object at the same time.
My questions are:
Is it also necessary to lock the object when reading from it?
Am I correct to think that if we just lock the object when writing, a critical section is enough; but if we lock the object when reading or writing, a mutex is necessary?
I am asking this question because in Microsoft Office, it is not possible for two instances of Word to access a document in read/write access mode; but while the document is being opened in read/write mode, it is possible to open another instance of Word to access the document in read only mode. Would the same logic apply in threading?
As Ofir already wrote - if you try to read data from an object that some other thread is modyfying - you could get data in some inconsistent state.
But - if you are sure the object is not being modified, you can of course read it from multiple threads. In general, the question you are asking is more or less the Readers-writers problem - see http://en.wikipedia.org/wiki/Readers-writers_problem
Lastly - a critical section is an abstract term and can be implemented using a mutex or a monitor. The syntax sugar for a critical section in java or C# (synchronized, lock) use a monitor under the covers.
Is it also necessary to lock the object when reading from it?
If something else could write to it at the same time - yes. If only another read could occur - no. In your circumstances, I would say - yes.
Am I correct to think that if we just lock the object when writing, a
critical section is enough; but if we
lock the object when reading or
writing, a mutex is necessary?
No, you can use a critical section for both, other things being equal. Mutexes have added features over sections (named mutexes can be used from multiple processes, for example), but I don't think you need such features here.
It is necessary, because otherwise (unless operations are atomic) you may be reading an intermediate state.
You may want to allow multiple readers at the same time which requires a (bit) more complex kind of lock.
depends on how you use and read it. if your read is atomic (i.e, won't be interrupted by write) and the read thread does not have dependency with the write threads, then you maybe able to skip read lock. But if your 'read' operation takes some time and takes heavy object interation, then you should lock it for read.
if your reading does not take a very long time (i.e., won't delay the write threads too long), critical section should be enough.
locking is only needed when two processes can change the same database table elements.
when you want to read data it is always secure. you read data of a consistent database. the process changing the data has a shadow version which is consistent and will override current data when you save it. but if you are running a reading process which is depending on critical value from database elements you should look for locks which indicates those values are likely to be altered. so your reading is delayed until the lock is gone.

Resources