Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1251625
  • 博文数量: 247
  • 博客积分: 5587
  • 博客等级: 大校
  • 技术积分: 2060
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-24 13:27
文章分类
文章存档

2012年(101)

2011年(44)

2010年(102)

分类:

2010-11-07 22:52:06


When we multiple threads running they will invariably need to communicate with each other in order synchronise their execution. This chapter describes the synchronization types available with threads and discusses when and how to use synchronization.

There are a few possible methods of synchronising threads:

  • Mutual Exclusion (Mutex) Locks
  • Condition Variables
  • Semaphores

We wil frequently make use of Synchronization objects: these are variables in memory that you access just like data. Threads in different processes can communicate with each other through synchronization objects placed in threads-controlled shared memory, even though the threads in different processes are generally invisible to each other.

Synchronization objects can also be placed in files and can have lifetimes beyond that of the creating process.

Here are some example situations that require or can profit from the use of synchronization:

  • When synchronization is the only way to ensure consistency of shared data.
  • When threads in two or more processes can use a single synchronization object jointly. Note that the synchronization object should be initialized by only one of the cooperating processes, because reinitializing a synchronization object sets it to the unlocked state.
  • When synchronization can ensure the safety of mutable data.
  • When a process can map a file and have a thread in this process get a record's lock. Once the lock is acquired, any other thread in any process mapping the file that tries to acquire the lock is blocked until the lock is released.
  • Even when accessing a single primitive variable, such as an integer. On machines where the integer is not aligned to the bus data width or is larger than the data width, a single memory load can use more than one memory cycle. While this cannot happen on the SPARC architectures, portable programs cannot rely on this.

Mutual exclusion locks (mutexes) are a comon method of serializing thread execution. Mutual exclusion locks synchronize threads, usually by ensuring that only one thread at a time executes a critical section of code. Mutex locks can also preserve single-threaded code.

Mutex attributes may be associated with every thread. To change the default mutex attributes, you can declare and initialize an mutex attribute object and then alter specific values much like we have seen in the last chapter on more general POSIX attributes. Often, the mutex attributes are set in one place at the beginning of the application so they can be located quickly and modified easily.

After the attributes for a mutex are configured, you initialize the mutex itself. Functions are available to initialize or destroy, lock or unlock, or try to lock a mutex.

The function pthread_mutexattr_init() is used to initialize attributes associated with this object to their default values. It is prototyped by:

int pthread_mutexattr_init(pthread_mutexattr_t *mattr);

Storage for each attribute object is allocated by the threads system during execution. mattr is an opaque type that contains a system-allocated attribute object. The possible values of mattr's scope arePTHREAD_PROCESS_PRIVATE (the default) and PTHREAD_PROCESS_SHARED.The default value of the pshared attribute when this function is called is PTHREAD_PROCESS_PRIVATE, which means that the initialized mutex can be used within a process.

Before a mutex attribute object can be reinitialized, it must first be destroyed by pthread_mutexattr_destroy() (see below). The pthread_mutexattr_init() call returns a pointer to an opaque object. If the object is not destroyed, a memory leak will result. pthread_mutexattr_init() returns zero after completing successfully. Any other returned value indicates that an error occurred.

A simple example of this function call is:

#include 

pthread_mutexattr_t mattr; 
int ret; 

/* initialize an attribute to default value */ 

ret = pthread_mutexattr_init(&mattr);

The function pthread_mutexattr_destroy() deallocates the storage space used to maintain the attribute object created by pthread_mutexattr_init(). It is prototyped by:

int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr);

which returns zero after completing successfully. Any other returned value indicates that an error occurred.

The function is called as follows:

#include 

pthread_mutexattr_t mattr; 
int ret; 

/* destroy an attribute */ 
ret = pthread_mutexattr_destroy(&mattr);

The scope of a mutex variable can be either process private (intraprocess) or system wide (interprocess). The function pthread_mutexattr_setpshared() is used to set the scope of a mutex atrribute and it is prototype as follows:

int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared);

If the mutex is created with the pshared (POSIX) attribute set to the PTHREAD_PROCESS_SHARED state, and it exists in shared memory, it can be shared among threads from more than one process. This is equivalent to the USYNC_PROCESS flag in mutex_init() in Solaris threads. If the mutex pshared attribute is set to PTHREAD_PROCESS_PRIVATE, only those threads created by the same process can operate on the mutex. This is equivalent to the USYNC_THREAD flag in mutex_init() in Solaris threads.

pthread_mutexattr_setpshared() returns zero after completing successfully. Any other returned value indicates that an error occurred.

A simple example call is:

#include  

pthread_mutexattr_t mattr; 
int ret; 

ret = pthread_mutexattr_init(&mattr); 

/* resetting to its default value: private */
ret = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE);

The function pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr, int *pshared) may be used to obtain the scope of a current thread mutex as follows:

 #include 
pthread_mutexattr_t mattr; 
int pshared, ret; 

/* get pshared of mutex */ ret =
pthread_mutexattr_getpshared(&mattr, &pshared);

The function pthread_mutex_init() to initialize the mutex, it is prototyped by:

int pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr);

Here, pthread_mutex_init() initializes the mutex pointed at by mp to its default value if mattr is NULL, or to specify mutex attributes that have already been set with pthread_mutexattr_init().

A mutex lock must not be reinitialized or destroyed while other threads might be using it. Program failure will result if either action is not done correctly. If a mutex is reinitialized or destroyed, the application must be sure the mutex is not currently in use. pthread_mutex_init() returns zero after completing successfully. Any other returned value indicates that an error occurred.

A simple example call is:

#include 

pthread_mutex_t mp = PTHREAD_MUTEX_INITIALIZER; 
pthread_mutexattr_t mattr; 
int ret; 

/* initialize a mutex to its default value */ 
ret = pthread_mutex_init(&mp, NULL);

When the mutex is initialized, it is in an unlocked state. The effect of mattr being NULL is the same as passing the address of a default mutex attribute object, but without the memory overhead. Statically defined mutexes can be initialized directly to have default attributes with the macro PTHREAD_MUTEX_INITIALIZER.

To initialise a mutex with non-default values do something like:

/* initialize a mutex attribute */ 
ret = pthread_mutexattr_init(&mattr);

/* change mattr default values with some function */
 ret = pthread_mutexattr_*();

/* initialize a mutex to a non-default value */
ret = pthread_mutex_init(&mp, &mattr);

The function pthread_mute_lock() is used to lock a mutex, it is prototyped by:

int pthread_mutex_lock(pthread_mutex_t *mp);

pthread_mute_lock() locks the mutex pointed to by mp. When the mutex is already locked, the calling thread blocks and the mutex waits on a prioritized queue. When pthread_mute_lock() returns, the mutex is locked and the calling thread is the owner. pthread_mute_lock() returns zero after completing successfully. Any other returned value indicates that an error occurred.

Therefor to lock a mutex mp on would do the following:

#include  
pthread_mutex_t mp; 
int ret; 

ret = pthread_mutex_lock(&mp);

To unlock a mutex use the function pthread_mutex_unlock() whose prototype is:

int pthread_mutex_unlock(pthread_mutex_t *mp);

Clearly, this function unlocks the mutex pointed to by mp.

The mutex must be locked and the calling thread must be the one that last locked the mutex (i.e. the owner). When any other threads are waiting for the mutex to become available, the thread at the head of the queue is unblocked. pthread_mutex_unlock() returns zero after completing successfully. Any other returned value indicates that an error occurred.

A simple example call of pthread_mutex_unlock() is:

#include  

pthread_mutex_t mp; 
int ret; 

/* release the mutex */ 
ret = pthread_mutex_unlock(&mp);

The function pthread_mutex_trylock() to attempt to lock the mutex and is prototyped by:

int pthread_mutex_trylock(pthread_mutex_t *mp);

This function attempts to lock the mutex pointed to by mppthread_mutex_trylock() is a nonblocking version of pthread_mutex_lock(). When the mutex is already locked, this call returns with an error. Otherwise, the mutex is locked and the calling thread is the owner. pthread_mutex_trylock() returns zero after completing successfully. Any other returned value indicates that an error occurred.

The function is called as follows:

#include  
pthread_mutex_t mp;

/* try to lock the mutex */
int ret; ret = pthread_ mutex_trylock(&mp);

The function pthread_mutex_destroy() may be used to destroy any state associated with the mutex. It is prototyped by:

int pthread_mutex_destroy(pthread_mutex_t *mp);

and destroys a mutex pointed to by mp.

Note: that the space for storing the mutex is not freed. pthread_mutex_destroy() returns zero after completing successfully. Any other returned value indicates that an error occurred.

It is called by:

#include  
pthread_mutex_t mp;
int ret; 

/* destroy mutex */
ret = pthread_mutex_destroy(&mp);

Here are some code fragments showing mutex locking.

We develop two small functions that use the mutex lock for different purposes.

  • The increment_count function() uses the mutex lock simply to ensure an atomic update of the shared variable, count.
  • The get_count() function uses the mutex lock to guarantee that the (long long) 64-bit quantity count is read atomically. On a 32-bit architecture, a long long is really two 32-bit quantities.

The 2 functions are as follows:

#include  
pthread_mutex_t count_mutex; 
long long count; 

void increment_count() 
   { pthread\_mutex\_lock(&count_mutex); 
     count = count + 1;
     pthread_mutex_unlock(&count_mutex); 
   }   


long long get_count()  
   { long long c;
     pthread\_mutex\_lock(&count_mutex); 
     c = count; 
     pthread_mutex_unlock(&count_mutex);
     return (c); 
    }

Recall that reading an integer value is an atomic operation because integer is the common word size on most machines.

You may occasionally want to access two resources at once. For instance, you are using one of the resources, and then discover that the other resource is needed as well. However, there could be a problem if two threads attempt to claim both resources but lock the associated mutexes in different orders.

In this example, if the two threads lock mutexes 1 and 2 respectively, then a deadlock occurs when each attempts to lock the other mutex.

Thread 1Thread 2
/* use resource 1 *//* use resource 2 */
pthread_mutex_lock(&m1);pthread_mutex_lock(&m2);
  
/* NOW use resources 2 + 1 *//* NOW use resources 1 + 2 */
  
pthread_mutex_lock(&m2);pthread_mutex_lock(&m1);
  
pthread_mutex_lock(&m1);pthread_mutex_lock(&m2);

The best way to avoid this problem is to make sure that whenever threads lock multiple mutexes, they do so in the same order. This technique is known as lock hierarchies: order the mutexes by logically assigning numbers to them. Also, honor the restriction that you cannot take a mutex that is assigned n when you are holding any mutex assigned a number greater than n.

Note: The lock_lint tool can detect the sort of deadlock problem shown in this example.

The best way to avoid such deadlock problems is to use lock hierarchies. When locks are always taken in a prescribed order, deadlock should not occur. However, this technique cannot always be used :

  • sometimes you must take the mutexes in an order other than prescribed.
  • To prevent deadlock in such a situation, use pthread_mutex_trylock(). One thread must release its mutexes when it discovers that deadlock would otherwise be inevitable.

The idea of Conditional Locking use this approach:

Thread 1:

pthread_mutex_lock(&m1); 
pthread_mutex_lock(&m2); 

/* no processing */
pthread_mutex_unlock(&m2);
pthread_mutex_unlock(&m1);

Thread 2:

for (; ;) {
  pthread_mutex_lock(&m2); 
  if(pthread_mutex_trylock(&m1)==0) 
    /* got it! */ 
    break; 
 /* didn't get it */ 
 pthread_mutex_unlock(&m2); 
 } 
/* get locks; no processing */
pthread_mutex_unlock(&m1); 
pthread_mutex_unlock(&m2);

In the above example, thread 1 locks mutexes in the prescribed order, but thread 2 takes them out of order. To make certain that there is no deadlock, thread 2 has to take mutex 1 very carefully; if it were to block waiting for the mutex to be released, it is likely to have just entered into a deadlock with thread 1. To ensure this does not happen, thread 2 calls pthread_mutex_trylock(), which takes the mutex if it is available. If it is not, thread 2 returns immediately, reporting failure. At this point, thread 2 must release mutex 2, so that thread 1 can lock it, and then release both mutex 1 and mutex 2.

We have met basic linked structues in Section , when using threads which share a linked list structure the possibility of deadlock may arise.

By nesting mutex locks into the linked data structure and a simple ammendment of the link list code we can prevent deadlock by taking the locks in a prescribed order.

The modified linked is as follows:

typedef struct node1 { 
     int value; 
     struct node1 *link;
     pthread_mutex_t lock; 
   } node1_t;

Note: we simply ammend a standard singly-linked list structure so that each node containing a mutex.

Assuming we have created a variable node1_t ListHead.

To remove a node from the list:

  • first search the list starting at ListHead (which itself is never removed) until the desired node is found.
  • To protect this search from the effects of concurrent deletions, lock each node before any of its contents are accessed.

    Because all searches start at ListHead, there is never a deadlock because the locks are always taken in list order.

  • When the desired node is found, lock both the node and its predecessor since the change involves both nodes.

    Because the predecessor's lock is always taken first, you are again protected from deadlock.

The C code to remove an item from a singly linked list with nested locking is as follows:

 

node1_t *delete(int value) 
  { node1_t *prev,
    *current; prev = &ListHead;
 
   pthread_mutex_lock(&prev->lock); 
   while ((current = prev->link) != NULL) 
     { pthread_mutex_lock(¤t->lock); 
       if (current->value == value) 
         { prev->link = current->link;
           pthread_mutex_unlock(¤t->lock); 
           pthread_mutex_unlock(&prev->lock);
           current->link = NULL; return(current); 
         } 
       pthread_mutex_unlock(&prev->lock);
       prev = current; 
      } 
    pthread_mutex_unlock(&prev->lock); 
    return(NULL); 
   }

 Similar mutual exclusion locks exist for in Solaris.

You should include the or libraries.

To initialize a mutex use int mutex_init(mutex_t *mp, int type, void *arg))mutex_init() initializes the mutex pointed to by mp. The type can be one of the following (note that arg is currently ignored).

USYNC_PROCESS
-- The mutex can be used to synchronize threads in this and other processes.
USYNC_THREAD
-- The mutex can be used to synchronize threads in this process, only.

Mutexes can also be initialized by allocation in zeroed memory, in which case a type of USYNC_THREAD is assumed. Multiple threads must not initialize the same mutex simultaneously. A mutex lock must not be reinitialized while other threads might be using it.

The function int mutex_destroy (mutex_t *mp) destroys any state associated with the mutex pointed to by mpNote that the space for storing the mutex is not freed.

To acquire a mutex lock use the function mutex_lock(mutex_t *mp) which locks the mutex pointed to by mp. When the mutex is already locked, the calling thread blocks until the mutex becomes available (blocked threads wait on a prioritized queue).

To release a mutex use mutex_unlock(mutex_t *mp) which unlocks the mutex pointed to by mp. The mutex must be locked and the calling thread must be the one that last locked the mutex (the owner).

To try to acquire a mutex use mutex_trylock(mutex_t *mp) to attempt to lock the mutex pointed to by mp. This function is a nonblocking version of mutex_lock()

Condition variables can be usedto atomically block threads until a particular condition is true. Condition variables are always used in conjunction with mutex locks:

  • With a condition variable, a thread can atomically block until a condition is satisfied.
  • The condition is tested under the protection of a mutual exclusion lock (mutex).
    • When the condition is false, a thread usually blocks on a condition variable and atomically releases the mutex waiting for the condition to change.
    • When another thread changes the condition, it can signal the associated condition variable to cause one or more waiting threads to wake up, acquire the mutex again, and reevaluate the condition.

Condition variables can be used to synchronize threads among processes when they are allocated in memory that can be written to and is shared by the cooperating processes.

The scheduling policy determines how blocking threads are awakened. For the default SCHED_OTHER, threads are awakened in priority order. The attributes for condition variables must be set and initialized before the condition variables can be used.

As with mutex locks, The condiotion variable attributes must be initialised and set (or set to NULL) before an actual condition variable may be initialise (with appropriat attributes) and then used.

The function pthread_condattr_init() initializes attributes associated with this object to their default values. It is prototyped by:

int pthread_condattr_init(pthread_condattr_t *cattr);

Storage for each attribute object, cattr, is allocated by the threads system during execution. cattr is an opaque data type that contains a system-allocated attribute object. The possible values of cattr's scope are PTHREAD_PROCESS_PRIVATE and PTHREAD_PROCESS_SHARED. The default value of the pshared attribute when this function is called is PTHREAD_PROCESS_PRIVATE, which means that the initialized condition variable can be used within a process.

Before a condition variable attribute can be reused, it must first be reinitialized by pthread_condattr_destroy(). The pthread_condattr_init() call returns a pointer to an opaque object. If the object is not destroyed, a memory leak will result.

pthread_condattr_init() returns zero after completing successfully. Any other returned value indicates that an error occurred. When either of the following conditions occurs, the function fails and returns the corresponding value.

A simple example call of this function is :

#include 

pthread_condattr_t cattr; 
int ret; 

/* initialize an attribute to default value */ 
ret = pthread_condattr_init(&cattr);

The function pthread_condattr_destroy() removes storage and renders the attribute object invalid, it is prototyped by:

int pthread_condattr_destroy(pthread_condattr_t *cattr);

pthread_condattr_destroy() returns zero after completing successfully and destroying the condition variable pointed to by cattr. Any other returned value indicates that an error occurred. If the following condition occurs, the function fails and returns the corresponding value.

The scope of a condition variable can be either process private (intraprocess) or system wide (interprocess), as with mutex locks. If the condition variable is created with the pshared attribute set to thePTHREAD_PROCESS_SHARED state, and it exists in shared memory, it can be shared among threads from more than one process. This is equivalent to the USYNC_PROCESS flag in mutex_init() in the original Solaris threads. If the mutex pshared attribute is set to PTHREAD_PROCESS_PRIVATE (default value), only those threads created by the same process can operate on the mutex. UsingPTHREAD_PROCESS_PRIVATE results in the same behavior as with the USYNC_THREAD flag in the original Solaris threads cond_init() call, which is that of a local condition variable.PTHREAD_PROCESS_SHARED is equivalent to a global condition variable.

The function pthread_condattr_setpshared() is used to set the scope of a condition variable, it is prototyped by:

int pthread_condattr_setpshared(pthread_condattr_t *cattr, int pshared);

The condition variable attribute cattr must be initialised first and the value of pshared is either PTHREAD_PROCESS_SHARED or PTHREAD_PROCESS_PRIVATE.

pthread_condattr_setpshared() returns zero after completing successfully. Any other returned value indicates that an error occurred.

A sample use of this function is as follows:

#include  

pthread_condattr_t cattr; 
int ret; 

/* Scope: all processes */ 
ret = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED); 

/* OR */
/* Scope: within a process */ 
ret = pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_PRIVATE);

The function int pthread_condattr_getpshared(const pthread_condattr_t *cattr, int *pshared) may be used to obtain the scope of a given condition variable.

The function pthread_cond_init() initializes the condition variable and is prototyped as follows:

int pthread_cond_init(pthread_cond_t *cv, const pthread_condattr_t *cattr);

The condition variable which is initialized is pointed at by cv and is set to its default value if cattr is NULL, or to specific cattr condition variable attributes that are already set withpthread_condattr_init(). The effect of cattr being NULL is the same as passing the address of a default condition variable attribute object, but without the memory overhead.

Statically-defined condition variables can be initialized directly to have default attributes with the macro PTHREAD_COND_INITIALIZER. This has the same effect as dynamically allocatingpthread_cond_init() with null attributes. No error checking is done. Multiple threads must not simultaneously initialize or reinitialize the same condition variable. If a condition variable is reinitialized or destroyed, the application must be sure the condition variable is not in use.

pthread_cond_init() returns zero after completing successfully. Any other returned value indicates that an error occurred.

Sample calls of this function are:

#include  

pthread_cond_t cv; 
pthread_condattr_t cattr; 
int ret; 

/* initialize a condition variable to its default value */ 
ret = pthread_cond_init(&cv, NULL); 

/* initialize a condition variable */ ret =
pthread_cond_init(&cv, &cattr);

The function pthread_cond_wait() is used to atomically release a mutex and to cause the calling thread to block on the condition variable. It is protoyped by:

int pthread_cond_wait(pthread_cond_t *cv,pthread_mutex_t *mutex);

The mutex that is released is pointed to by mutex and the condition variable pointed to by cv is blocked.

pthread_cond_wait() returns zero after completing successfully. Any other returned value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.

A simple example call is:

#include  

pthread_cond_t cv; 
pthread_mutex_t mutex; 
int ret; 

/* wait on condition variable */ 
ret = pthread_cond_wait(&cv, &mutex);

The blocked thread can be awakened by a pthread_cond_signal(), a pthread_cond_broadcast(), or when interrupted by delivery of a signal. Any change in the value of a condition associated with the condition variable cannot be inferred by the return of pthread_cond_wait(), and any such condition must be reevaluated. The pthread_cond_wait() routine always returns with the mutex locked and owned by the calling thread, even when returning an error. This function blocks until the condition is signaled. It atomically releases the associated mutex lock before blocking, and atomically acquires it again before returning. In typical use, a condition expression is evaluated under the protection of a mutex lock. When the condition expression is false, the thread blocks on the condition variable. The condition variable is then signaled by another thread when it changes the condition value. This causes one or all of the threads waiting on the condition to unblock and to try to acquire the mutex lock again. Because the condition can change before an awakened thread returns from pthread_cond_wait(), the condition that caused the wait must be retested before the mutex lock is acquired.

The recommended test method is to write the condition check as a while loop that calls pthread_cond_wait(), as follows:

pthread_mutex_lock();

while(condition_is_false) 
  pthread_cond_wait(); 
pthread_mutex_unlock();

No specific order of acquisition is guaranteed when more than one thread blocks on the condition variable. Note also that pthread_cond_wait() is a cancellation point. If a cancel is pending and the calling thread has cancellation enabled, the thread terminates and begins executing its cleanup handlers while continuing to hold the lock.

To unblock a specific thread use pthread_cond_signal() which is prototyped by:

int pthread_cond_signal(pthread_cond_t *cv);

This unblocks one thread that is blocked on the condition variable pointed to by cvpthread_cond_signal() returns zero after completing successfully. Any other returned value indicates that an error occurred.

You should always call pthread_cond_signal() under the protection of the same mutex used with the condition variable being signaled. Otherwise, the condition variable could be signaled between the test of the associated condition and blocking in pthread_cond_wait(), which can cause an infinite wait. The scheduling policy determines the order in which blocked threads are awakened. ForSCHED_OTHER, threads are awakened in priority order. When no threads are blocked on the condition variable, then calling pthread_cond_signal()l has no effect.

The folloowing code fragment illustrates how to avoid an infinite problem described above:

pthread_mutex_t count_lock; 
pthread_cond_t count_nonzero; 
unsigned count; 

decrement_count() 
   { pthread_mutex_lock(&count_lock); 

     while (count == 0) 
        pthread_cond_wait(&count_nonzero, &count_lock); 
     count = count - 1; 
     pthread_mutex_unlock(&count_lock); 
   } 

increment_count() 
   { pthread_mutex_lock(&count_lock); 
     if (count == 0)
       pthread_cond_signal(&count_nonzero); 
     count = count + 1;
     pthread_mutex_unlock(&count_lock); 
    }

You can also block until a specified event occurs. The function pthread_cond_timedwait() is used for this purpose. It is prototyped by:

int pthread_cond_timedwait(pthread_cond_t *cv,
      pthread_mutex_t *mp, const struct timespec *abstime);

pthread_cond_timedwait() is used in a similar manner to pthread_cond_wait()pthread_cond_timedwait() blocks until the condition is signaled or until the time of day, specified by abstime, has passed. pthread_cond_timedwait() always returns with the mutex, mp, locked and owned by the calling thread, even when it is returning an error. pthread_cond_timedwait() is also a cancellation point.

pthread_cond_timedwait() returns zero after completing successfully. Any other returned value indicates that an error occurred. When either of the following conditions occurs, the function fails and returns the corresponding value.

An examle call of this function is:

#include  
#include  

pthread_timestruc_t to;
pthread_cond_t cv; 
pthread_mutex_t mp;
timestruct_t abstime; 
int ret; 

/* wait on condition variable */ 

ret = pthread_cond_timedwait(&cv, &mp, &abstime);


pthread_mutex_lock(&m); 
to.tv_sec = time(NULL) + TIMEOUT; 
to.tv_nsec = 0; 

while (cond == FALSE) 
  {  err = pthread_cond_timedwait(&c, &m, &to); 
     if (err == ETIMEDOUT) 
       { /* timeout, do something */ 
         break; 
       } 
   } 
pthread_mutex_unlock(&m);

All threads may be unblocked in one function: pthread_cond_broadcast(). This function is prototyped as follows:

int pthread_cond_broadcast(pthread_cond_t *cv);

pthread_cond_broadcast() unblocks all threads that are blocked on the condition variable pointed to by cv, specified by pthread_cond_wait(). When no threads are blocked on the condition variable, pthread_cond_broadcast() has no effect.

pthread_cond_broadcast() returns zero after completing successfully. Any other returned value indicates that an error occurred. When the following condition occurs, the function fails and returns the corresponding value.

Since pthread_cond_broadcast() causes all threads blocked on the condition to contend again for the mutex lock, use carefully. For example, use pthread_cond_broadcast() to allow threads to contend for varying resource amounts when resources are freed:

#include 

pthread_mutex_t rsrc_lock; 
pthread_cond_t rsrc_add;
unsigned int resources; 

get_resources(int amount) 
  { pthread_mutex_lock(&rsrc_lock);
    while (resources < amount) 
       pthread_cond_wait(&rsrc_add, &rsrc_lock); 
        
   resources -= amount; 
   pthread_mutex_unlock(&rsrc_lock); 
  } 

add_resources(int amount) 
  { pthread_mutex_lock(&rsrc_lock); 
    resources += amount;
    pthread_cond_broadcast(&rsrc_add); 
    pthread_mutex_unlock(&rsrc_lock); 
  }

Note: that in add_resources it does not matter whether resources is updated first or if pthread_cond_broadcast() is called first inside the mutex lock. Call pthread_cond_broadcast() under the protection of the same mutex that is used with the condition variable being signaled. Otherwise, the condition variable could be signaled between the test of the associated condition and blocking inpthread_cond_wait(), which can cause an infinite wait.

The function pthread_cond_destroy() to destroy any state associated with the condition variable, it is prototyped by:

int pthread_cond_destroy(pthread_cond_t *cv);

The condition variable pointed to by cv will be destroyed by this call:

#include  

pthread_cond_t cv; 
int ret; 

/* Condition variable is destroyed */ 
ret = pthread_cond_destroy(&cv);

Note that the space for storing the condition variable is not freed.

pthread_cond_destroy() returns zero after completing successfully. Any other returned value indicates that an error occurred. When any of the following conditions occur, the function fails and returns the corresponding value.

Similar condition variables exist in Solaris. The functions are prototyped in .

To initialize a condition variable use int cond_init(cond_t *cv, int type, int arg) which initializes the condition variable pointed to by cv. The type can be one of USYNC_PROCESS orUSYNC_THREAD (See Solaris mutex (Section  for more details). Note that arg is currently ignored.

Condition variables can also be initialized by allocation in zeroed memory, in which case a type of USYNC_THREAD is assumed. Multiple threads must not initialize the same condition variable simultaneously. A condition variable must not be reinitialized while other threads might be using it.

To destroy a condition variable use int cond_destroy(cond_t *cv) which destroys a state associated with the condition variable pointed to by cv. The space for storing the condition variable is not freed.

To wait for a condition use int cond_wait(cond_t *cv, mutex_t *mp) which atomically releases the mutex pointed to by mp and to cause the calling thread to block on the condition variable pointed to by cv.

The blocked thread can be awakened by cond_signal(cond_t *cv)cond_broadcast(cond_t *cv), or when interrupted by delivery of a signal or a fork. Use cond_signal() to unblock one thread that is blocked on the condition variable pointed to by cv. Call this function under protection of the same mutex used with the condition variable being signaled. Otherwise, the condition could be signaled between its test and cond_wait(), causing an infinite wait. Use cond_broadcast() to unblock all threads that are blocked on the condition variable pointed to by cv. When no threads are blocked on the condition variable then cond_broadcast() has no effect.

Finally, to wait until the condition is signaled or for an absolute time use int cond_timedwait(cond_t *cv, mutex_t *mp, timestruct_t abstime) Use cond_timedwait() as you would usecond_wait(), except that cond_timedwait() does not block past the time of day specified by abstime. cond_timedwait() always returns with the mutex locked and owned by the calling thread even when returning an error.

Chapter  has dealt with semaphore programming for POSIX and System V IPC semaphores.

Semaphore operations are the same in both POSIX and Solaris. The function names are changed from sema_ in Solaris to sem_ in pthreads. Solaris semaphore are defined in .

In this section we give a brief description of Solaris thread semaphores.

To initialize the function int sema_init(sema_t *sp, unsigned int count, int type, void *arg) is used. sematype can be one of the following ):

USYNC_PROCESS
-- The semaphore can be used to synchronize threads in this process and other processes. Only one process should initialize the semaphore.
USYNC_THREAD
-- The semaphore can be used to synchronize threads in this process.

arg is currently unused.

Multiple threads must not initialize the same semaphore simultaneously. A semaphore must not be reinitialized while other threads may be using it.

To increment a Semaphore use the function int sema_post(sema_t *sp)sema_post atomically increments the semaphore pointed to by sp. When any threads are blocked on the semaphore, one is unblocked.

To block on a Semaphore use int sema_wait(sema_t *sp)sema_wait() to block the calling thread until the count in the semaphore pointed to by sp becomes greater than zero, then atomically decrement it.

To decrement a Semaphore count use int sema_trywait(sema_t *sp)sema_trywait() atomically decrements the count in the semaphore pointed to by sp when the count is greater than zero. This function is a nonblocking version of sema_wait().

To destroy the Semaphore state call the function sema_destroy(sema_t *sp)sema_destroy() to destroy any state associated with the semaphore pointed to by sp. The space for storing the semaphore is not freed.



Dave Marshall 
1/5/1999
阅读(1584) | 评论(0) | 转发(0) |
0

上一篇:一个简单的linux线程池

下一篇:初试msql

给主人留下些什么吧!~~