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

    The other major component of any threads library, and certainly the case with POSIX threads, is

    the presence of a condition variable. Condition variables are useful when some kind of signaling

    must take place between threads, if one thread is waiting for another to do something before it

    can continue. Two primary routines are used by programs wishing to interact in this way:

    int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
    int pthread_cond_signal(pthread_cond_t* cond);

    To use a condition variable, one has to in addition have a lock that is associated with this condition

    . When calling either of the above routines this lock should be locked.

    The first routine, pthread_cond_wait(), puts the calling thread to sleep, and thus waits for some 

    other thread to signal it, usually when something in the program has changed that the now-

    sleeping thread might care about. A typical usage looks like this:

    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
    
    pthread_mutex_lock(&lock);
    while (ready == 0) {
            pthread_cond_wait(&cond, &wait);
    }
    pthread_mutex_unlock(&lock);

    In this code, after initialization of the relevant lock and condition, a thread checks to see if the

    variable ready has yet been set to something other than zero. If not, the thread simply calls the

    wait routine in order to sleep until some other thread wakes it.

    The code to wake a thread, which would run in some other thread, looks like this:

    pthread_mutex_lock(&lock);
    ready = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&lock);

    A few things to note about this code sequence. First, when signaling as well as when modifying

    the global variable ready, we always make sure to have the lock held. This ensures that we do

    not accidentally introduce a race condition into our code.

    Second, you might notice that the wait call takea a lock as its second parameter, whereas signal

    call only takes a condiiton. The reason for this difference is that the wait call, in addition to putting

    the calling thread to sleep, release the lock when putting said caller to sleep. Imagine if it did not:

    how could the other thread acquire the lock and signal it to wake up? However, before returning

    after being woken, the pthread_cond_wait() reacquires the lock, thus ensuring that any time the

    waiting thread is running between the lock acquire at the beginning of the wait sequence, and

    the lock release at the end, it holds the lock.

    On last oddity: the waiting thread re-checks the condition in a while loop, instead of a simple if

    statement. We will discuss this issue in detail when we study condiiton variables in a future

    chapter, but in general, using a while loop is the simple and safe thing to do. Although it rechecks

    the condition perhaps adding a little overhead, there are some pthread implementations that

    could spuriously wake up a  waiting thread; in such a case, without rechecking, the waiting thread

    will continue thinking that the condition has changed even though it has not. It is safer thus to

    view waking up as a hint that something might have changed, rather than an absolute fact.

    Note that sometines it is temping to use a simple flag to signal between two threads, instead of

    a condition variable and associated lock. For example, we could rewrite the waiting code above to

    look more like this in the waiting code:

    while (ready == 0)
            ;

    Don't ever do this, for the following reasons. First, it performas poorly in many cases spinning for

    a long time just wastes CPU cycles. Second, it is error prone. As recent research shows, it is

    surprisingly easy to make mistaks when using flags as above to synchronize between threads;

    in that study, roughly half the uses of these ad hoc synchronizations were buggy! Don't be lazy;

    use condition variables even when you think you can get away without doing so.

    If condition variables sound confusing, don't worry too much yet, we will be covering them in

    great detail in a subsequent chapter. Until then, it should suffice to know that they exist and to

    have some idea how and why they are used.

                        Compiling and Running

    All of the code examples in this chapter are relatively easy to get up and running. To compile them

    , you must include the header pthread.h in your code. On the link line, you must also explicitly link

    with the pthreads libraary, by adding the -pthread flag.

    For example, to compile a simple multi-threaded program, all you have to do is the following:

    prompt> gcc -o main main.c -Wall -pthread

    As long as main.c includes the pthreads header, you have now successfully compiled a concurrent

    program. Whether it works or not, as usual, is a different matter entirely.

    We have introduced the basics of the pthread library, including thread creation, building mutual

    exclusion via locks, and signaling and waiting via condition variables. You don't need much else

    to write robust and efficient multi-threaded code, except patience and a great deal of care.

    We now end the chapter with a set of tips that might be useful to you when you write multi-

    threaded code see the aside on the following page for details. There are other aspects of the API

    that are interesting; if you want some more information, type man -k pthread on a linux system

    to see over a hundred APIs that make up the entire interface. However, the basics discussed 

    herein should enable you to build sophisticated and hopefully, correct and perfromant multi-

    threaded programs. The hard part with threads is not the APIs, but rather the tricky logic of how

    you build concurrent programs. Read on to learn more.

                        Aside: Thread API Guidelines

    There are a number of small but important things to remember when use the POSIX thread library

    or really, any thread library to build a multi-threaded program. They are:

    keep it small. Above all else, any code to lock or signal between threads should be as simple as

    possible. Tricky thread interactions lead to bugs.

    Minimize thread interactions. Try to keep the number of ways in which threads interact to a minimum.

    Each interaction should be carefully thought out and constructed with tried and true approaches many

    of which we will learn about in the coming chapters.

    Initialize locks and condition variables. Failure to do so will lead to code that sometimes works and

    sometimes fails in very strange ways.

    Check your return codes. Of course, in any C and UNIX programming you do, you should be checking

    each and every return code, and it's true here as well. Failure to do so will lead to bizarre and hard to

    understand behavior, making you likely to scream, pull some of your hair, or both.

    Be careful with how you pass arguments to, and return values from, threads. In particular, any time

    you are passing a reference to a variable allocated on the stack, you aer probably doing something

    wrong.

    Each thread has its stack. As related to the point above, please remember that each thread has its

    own stack. Thus, if you have a locally-allocated variable inside of some function a thread is executing

    , it is essentially private to that thread; no other thread can easily access it. To share data between

    threads, the values must be in the heap or othrewise some locale that is globally accessible.

    Always use condition variables to signal between threads. While it is often tempting to use simple

    flag, don't do it.

    Use the manual pages. On linux, in particular, the pthread man pages are highly informative and

    discuss much of nuances presented here, often in even more detail, read them carefully!

  • 相关阅读:
    10.$和#的区别 | 11.添加对象时如何把生成的id返回
    9. Mybatis的分页助手(pageHelper):
    8.逆向工程
    7.动态Sql语句
    6.关联查询
    5.解决列名与属性名不一致的情况
    4.接口与映射文件结合使用
    3.优化Mybatis
    2、Mybatis的编程Demo
    阿帕奇ab测试
  • 原文地址:https://www.cnblogs.com/miaoyong/p/4977316.html
Copyright © 2011-2022 走看看