zoukankan      html  css  js  c++  java
  • Operating System: Three Easy Pieces --- Locks (Note)

    Beyond thread creation and join, probably the next most useful set of functions provided by the

    POSIX threads library are those for providing mutual exclusion to a critical section via locks. The

    most basic pair of routines to use for this purpose is provided by this pair of routines:

    int pthread_mutex_lock(pthread_mutex_t* mutex);
    int pthread_mutex_unlock(pthread_mutex_t* mutex);

    The routines should be easy to understand and use. When you have a region of code you realize

    is a critical section, and thus needs to to be protected by locks in order to operate as desired. You

    can probably imagine what the code looks like:

    pthread_mutex_t lock;
    pthread_mutex_lock(&lock);
    x = x + 1;
    pthread_mutex_unlock(&lock);

    The intent of the code is as follows: if no other thread holds the lock when pthread_mutex_lock()

    is called, the thread will acquire the lock and enter the critical section. If another thread does 

    indeed hold the lock, the thread trying to grab the lock will not return from the call until it has

    acquired the lock implying that the thread holding the lock has released it via the unlock call.

    Of course, many threads may be stuck waiting inside the lock acquisition function at a given

    time; only the thread with the lock acquired, however, should call unlock.

    Unfortunately, this code is broken, in two improtant ways. The first problem is a lack of proper

    initialization. All locks must be properly initialized in order to guarantee that they are correct

    values to begin with and thus work as desired when lock and unlock are called.

    With POSIX threads, there are two ways to initialize locks. One way to do this is to use

    PTHREAD_MUTEX_INITIALIZER, as follows:

    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

    Doing so sets the lock to the default values and thus makes the lock usable. The dynamic way

    to do it at run time is to make a call to pthread_mutex_init(), as follows:

    int rc = pthread_mutex_init(&lock, NULL);
    assert(rc  == 0);

    The first argument to this routine is the address of the lock itself, whereas the second is an 

    optional set of attributes. Read more about the attributes yourself; passing NULL in simple uses

    the defaults. Either way works, but we usually use the dynamic latter method. Note that a 

    corresponding call to pthread_mutex_destroy() should also be made, when you are done with the

    lock; see the manual page for all of details.

    The second problem with the code above is that it fails to check error codes when calling lock and

    unlock. Just like virtually any library routine you call in a UNIX system, these routines can also

    fail. If your code doesn't properly check error codes, the failure will happen silently, which in this

    case could allow multiple threads into a critical section. Minimally, use wrappers, which assert 

    that the routine succeed; more sophisticated programs, which cann't simple exit when something

    goes wrong, should check for failure and do something appropriate when the lock or unlock 

    does not succeed.

    The lock and unlock routines are not only routines within the pthreads library to interact with

    locks. In particular, here are two more routines which may be of interest:

    int pthread_mutex_trylock(pthread_mutex_t* mutex);
    int pthread_mutex_timedlock(pthread_mutex_t* mutex, 
                                                struct timespec* abs_timeout);

    These two calls are used in lock acquisition. The trylock version returns failure if the lock is 

    already held; the timedlock version of acquiring a lock returns after a timeout or after acquiring

    the lock, whichever happens first. Thus, the timedlock with a timeout of zero degenerates to the

    trylock case. Both of these versions should generally be avoided; however, there are a few cases

    where avoiding getting stuck (perhaps indefinitely) in a lock acquisition routine can be useful,

    as we'll see in future chapters when we study deadlock.

  • 相关阅读:
    ubuntu应用商店打不开怎么办
    java线程池系列(1)-ThreadPoolExecutor实现原理
    java并发之SynchronousQueue实现原理
    java并发等待条件的实现原理(Condition)
    轻松掌握java读写锁(ReentrantReadWriteLock)的实现原理
    Java NIO 内存映射文件
    Java NIO Path接口和Files类配合操作文件
    Java 序列化 序列化与单例模式 [ 转载 ]
    Java 序列化 JDK序列化总结
    Java 泛型 Java使用泛型的意义
  • 原文地址:https://www.cnblogs.com/miaoyong/p/4977280.html
Copyright © 2011-2022 走看看