affect the overall result. For example, a global counter is a value on which
to calculate the average number of requests to the system per day, and you
can ignore the error of registering such requests
that occur every few
hours. Unfortunately, in most cases, this approach is not accepted.
Sometimes, the use of global data is not dictated by the specifics of the
task.
In this case, the straightforward solution is to create local copies of
data for each stream and operate them only. In practice, this works very
well and should be used wherever possible. For example, if the specifics of
the problem allow the creation of a separate counter for each thread (or
global array of counters, where each element is changed only by a certain
flow), the implementation of such
data structures solves the
problem. Again, such a solution cannot be applied in all cases (for example,
in the situation with bank accounts, different flows, after all, must somehow
modify the total for all accounts).
In all other cases, changes must be protected against the effects of other
flows. This is the main task of synchronization. Consider different
approaches to solving it.
Critical Sections And Locks
The Concept Of Critical Section
Consider using a simpler idea to solve a competition problem. It is easy to
see how the source of our mistake is that, from the outside, the simplest
money-transferring operation actually breaks down into several operations,
with the chance of any other flow interfering between them. In this case, it
is said that the operation is not an atom but a molecule.
It follows that the solution to the problem of competition is to transform the
code snippet that causes the problem into an atomic operation, that is, one
that is guaranteed to be performed completely without interference from
other threads. This
piece of code called a critical section :
// start
critical section
total_amount = total_amount + new_amount;
// end of critical section
Now, when two threads are going to execute the code of the critical section
at the same time, the one that started the first one will execute all its code
completely before the second starts its execution (the second thread will
wait until the first one completes the code of the critical section). As a
result, we are guaranteed to have a sequence of events in Option 2 in our
program, and the competition will never take place.
Consider the properties that a critical section should have.
❖
Mutual exclusion: In a particular time code, a critical section can
execute
only one thread
❖
Progesterone if multiple threads are present at the entrance to the
critical section, one of them must be necessarily required to enter into
it (they cannot block each other completely)
❖
Limited mode, a process that tries to enter a critical section, sooner
or later is necessarily required to enter into it
The simple question remains: "How can we make the system perceive
multiple operations as one atomic operation?". The simplest
solution to such a task would be to prohibit interruptions for the duration of
the critical section. This approach, although solves the problem in principle,
in practice, cannot be applied as a result because the cycling program in the
critical section of the whole system can remain as locked interrupts, and
therefore, present an unfavorable condition.
Locking
The
tidier solution is to use locks. A lock is a mechanism that prevents more
than one thread from executing the code of a critical section. Using a lock
comes in two steps: commit (lock, acquire _ lock ()) and unlock
(lock, release _ lock ()). In the case of a lock, it is checked that it has not
already been made by another thread, and if so,
this thread enters the
standby state, otherwise, it enforces the lock and enters the critical
section. After leaving the critical section, the flow removes the blockage .
acquire _ lock ( lock );
// critical section
release_lock (lock);
So realize mutual exclusion property, hence another name for blocking
is mutex, short for mutual exclusion.