• 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.

  • 相关阅读:
    堆的创建、优先队列、topk、堆排序C语言实现
    HTTPS加密原理
    go shard map实现
    Python进程间通信
    TCP 半连接队列和全连接队列
    WireShark过滤语法
    TCP拥塞机制
    【企业管理实务系列】低值易耗品管理办法
    CV之Face Change:基于人工智能实现国内众多一线美女明星换脸(基于Face++输出4*106个特征点定位+融合代码、deepfake技术)
    【转发】农行银企直联XML对接socket SAP EPIC
  • 原文地址:https://www.cnblogs.com/miaoyong/p/4977280.html
Copyright © 2020-2023  润新知