[]-Talk from the underlying implementation of the lock look 2

Foreword

Case my last blog, the thread requesting the lock if the lock has been found occupied by other threads, it is by way of spin-wait, that is constantly trying until it succeeds. Benpian will discuss another way, and that is suspended to await wake.

Note: The relevant code from the "Operating System: Three Easy Pieces" book.

Spin where the bad?

First explain, spin also has its benefits, but do not speak here first, we talk about what issues it might exist.

We consider an extreme scenario, a computer has only one CPU, this time there are two threads compete lock, thread A get locked into a critical section, the critical section of code is started (Since only one CPU, A thread of execution when thread B can only wait in the ready queue). A result is not finished execution thread critical section of code, the time slice to run out, so a context switch occurs, the thread A is changed out, now starts executing the thread B, B will start the thread attempts to acquire a lock.

This time embarrassment came with the lock thread is not running, it can not release the lock. While occupying the CPU thread due not obtain lock, you can only spin until it runs out of time slices.

This is only the case of two threads, waiting threads if there are more than 100 of it, that in the polling scheduler scene, thread A is not to wait until all of this in more than 100 threads to run completely idle, which wastes may big!

How to use yield () to let the CPU?

 yield () method is cut out between the calling thread back into the ready queue. This method is different from the previous is that, when the thread B resumes, it will only try once, if that fails, then exit without running out its entire time slice. That thread will be scheduled to try more than once. While this would be better than a little spin. But the cost is not small, the case for more than 100 waiting threads, each should be carried out once run-and-yield operations. Context switching overhead is not to be underestimated.

Direct hang, waiting for wake

The reason why there will be front excessive context switching, because waiting threads will still keep trying, but not before Bale less frequent.

They will not let these waiting threads do not like?

Ah, just to the threads out of the ready queue, they will not be the OS scheduler, it will not be run.

Can hang up, you have to think about who is going to wake up, wake up how?

Wake-up operation certainly is handled by a thread releases the lock. On the other hand, we have a thread hangs when certainly have to use a data structure to record the information in this thread, or to wake up when the wake-up who do not know. And this certainly takes a lock object data structure associated with it, such a thread releases the lock will know where to get the data.

typedef struct __lock_t {
     int In Flag; // identifier, whether the lock is occupied 
    int Guard; // guard field 
    a queue_t, print * Q; // wait queue, for storing thread information waiting 
} lock_t; 

void lock_init (lock_t * m) { 
    m -> In Flag = 0 ; 
    m -> Guard = 0 ; 
    queue_init (m -> Q); 
} 

void  Lock (lock_t * m) {
     the while (TestAndSet (& M-> Guard, . 1 ) == . 1 ) 
        ; // by self rotary obtain Guard 
    IF(M-> In Flag == 0 ) { 
        m -> In Flag = . 1 ; 
        m -> Guard = 0 ; 
    } the else { 
        queue_add (m -> Q, gettid ()); 
        m -> Guard = 0 ; // NOTE: in the Park () before calling 
        Park (); // before Park () call has been successfully queued thread 
    } 
} 

void UNLOCK (lock_t * m) {
     the while (TestAndSet (& M-> Guard, . 1 ) == . 1 ) 
        ; / / acquired by the spin guard 
    IF (queue_empty (M-> Q))// If there are no waiting threads, then the lock identified as "idle" 
        M-> In Flag = 0 ; 
     the else 
        to unpark (queue_remove (m -> Q)); // wake a thread waiting, then the lock identification is still "has occupied " 
    M-> Guard = 0 ; 
}

park()与unpark(threadID)

park () and unpark (threadID) is a Solaris system provides primitives for suspend and resume threads. Other systems will also provide general, but the details may differ.

park () => the current calling thread is suspended

uppark (threadID) => The thread specified wake-up thread ID.

Use guard field

I have a look at this code when a doubt that this is where queue_t defined, which in the end is what? The queue internal synchronous operation is not done? Sync word, a plurality of threads simultaneously access, data queue structure could be destroyed. In fact, a closer look at the code you will find, at the time of the operation of the queue, the thread need to get guard. In other words, the same time only one thread can access the queue. So this queue is safe, it itself does not need to provide synchronization. So, it did not put the book on the source code. A queue just to achieve it.

Actually guard field is used to control access to multi-threaded lock objects, only one thread at a time can make changes to other information lock objects (except for guard field).

Problems in the above code

From the Code, when the guard was released, the other threads can access the Lock objects. It may be a case that the release of the guard, but not enough time to perform the park () occurs context switch. What's wrong with it this time, we look at the chart:

Due to context switching, Thread A has joined the queue, but does not perform the pending operation. When the results thread releases the lock of possession, just removed from the queue Thread A, Thread A wake up, put in the ready queue, wait until the next scheduled execution time. Thread A recovery continues downward, call park () method. The result is Thead A permanently suspended! ! ! . Because this time it has been removed from the waiting queue, who do not know that it is hung up.

Solutions provided by the OS

OS provides a setpark () function to identify a thread to be executed park () operation. If performed in this park thread (such as Thread A) () before the operation, other threads (e.g., Thread B) is performed is unpark (threadID) method, the thread (Thread A) performing park () returns immediately. Change as follows:

...
queue_add(m->q, gettid());
setpark();
m->guard=0;
park();
...

PS: Actually this setpark () function should only set a flag at the bottom of the Thread object, park () function inside look at the flag. But the underlying Thread the object of our visit unnoticed.

 

Guess you like

Origin www.cnblogs.com/longfurcat/p/11107892.html