Wednesday, April 28, 2010

My understanding of Thread Sync Objects

Mutexes:
A mutex is a special lock that only one thread may lock at a time. If a thread locks a mutex and then a second thread also tries to lock the same mutex, the second thread is blocked, or put on hold. Only when the first thread unlocks the mutex is the second thread unblocked—allowed to resume execution. GNU/Linux guarantees that race conditions do not occur among threads attempting to lock a mutex; only one thread will ever get the lock, and all other threads will be blocked.

A thread may attempt to lock a mutex by calling pthread_mutex_lock on it. If the mutex was unlocked, it becomes locked and the function returns immediately. If the mutex was locked by another thread, pthread_mutex_lock blocks execution and returns only eventually when the mutex is unlocked by the other thread. More than one thread may be blocked on a locked mutex at one time.When the mutex is unlocked, only one of the blocked threads (chosen unpredictably) is unblocked and allowed to lock the mutex; the other threads stay blocked.

pthread_mutex_t mutex;
pthread_mutex_init (&mutex, NULL);
pthread_mutex_lock (&mutex);
pthread_mutex_unlock (&mutex);

A simple type of deadlock may occur when the same thread attempts to lock a mutex twice in a row.

Locking a fast mutex (the default kind) will cause a deadlock to occur.

Locking a recursive mutex does not cause a deadlock. (those many number of calls to unlock the mutex needs to be made, in order to unlock the mutex, that’s it.)

GNU/Linux will detect and flag a double lock on an error-checking mutex that would otherwise cause a deadlock.The second consecutive call to pthread_mutex_lock returns the failure code EDEADLK.

By default, a GNU/Linux mutex is of the fast kind, the other two types can be created as –

This code sequence illustrates creation of an error-checking mutex, for instance:
pthread_mutexattr_t attr;
pthread_mutex_t mutex;
pthread_mutexattr_init (&attr);
pthread_mutexattr_setkind_np (&attr, PTHREAD_MUTEX_ERRORCHECK_NP); // or PTHREAD_MUTEX_RECURSIVE_NP
pthread_mutex_init (&mutex, &attr);
pthread_mutexattr_destroy (&attr);

pthread_mutex_trylock() : If you call pthread_mutex_trylock on an unlocked mutex, you will lock the mutex as if you had called pthread_mutex_lock, and pthread_mutex_trylock will return zero. However, if the mutex is already locked by another thread, pthread_mutex_trylock will not block. Instead, it will return immediately with the error code EBUSY.

Example:
thread_function()
{
While(1)
{
pthread_mutex_lock (&job_queue_mutex);
do_task()
pthread_mutex_unlock (&job_queue_mutex);
}
}

Semaphores:

Why is the need of Semaphore?
In the example, in which several threads process jobs from a queue, the main thread function of the threads carries out the next job until no jobs are left and then exits the thread. This scheme works if all the jobs are queued in advance or if new jobs are queued at least as quickly as the threads process them. However, if the
threads work too quickly, the queue of jobs will empty and the threads will exit. If new jobs are later enqueued, no threads may remain to process them. What we might like instead is a mechanism for blocking the threads when the queue empties until new jobs become available. A semaphore provides a convenient method for doing this. A semaphore is a counter that can be used to synchronize multiple threads.

Example:
thread_function()
{
While(1)
{
sem_wait(&job_queue_count);
pthread_mutex_lock (&job_queue_mutex);
do_task()
pthread_mutex_unlock (&job_queue_mutex);
}
}
some_other_async_func()
{
pthread_mutex_lock (&job_queue_mutex);
do_some_other_related_task()
sem_post (&job_queue_count);
pthread_mutex_unlock (&job_queue_mutex);
}

Important semaphore details –
semaphore.h
sem_t
sem_init()
sem_destroy()
sem_wait()
sem_trywait()
sem_post()
sem_getvalue()

Condition Variables:
Why do we need that?
Suppose that you write a thread function that executes a loop infinitely, performing some work on each iteration.The thread loop, however, needs to be controlled by a flag: The loop runs only when the flag is set; when the flag is not set, the loop pauses. During each iteration of the loop, the thread function checks that the flag is set. Because the flag is accessed by multiple threads, it is protected by a mutex. This implementation may be correct, but it is not efficient.The thread function will spend lots of CPU whenever the flag is not set, checking and rechecking the flag, each time locking and unlocking the mutex. What you really want is a way to put the thread to sleep when the flag is not set, until some circumstance changes that might cause the flag to become set.

How does condition variable work?
A condition variable enables you to implement a condition under which a thread executes and, inversely, the condition under which the thread is blocked. As long as every thread that potentially changes the sense of the condition uses the condition variable properly, Linux guarantees that threads blocked on the condition will be unblocked when the condition changes.
As with a semaphore, a thread may wait on a condition variable. If thread A waits on a condition variable, it is blocked until some other thread, thread B, signals the same condition variable. Unlike a semaphore, a condition variable has no counter or memory; thread A must wait on the condition variable before thread B signals it. If thread B signals the condition variable before thread A waits on it, the signal is lost, and thread A blocks until some other thread signals the condition variable again.

Important condition variable routines –
pthread_cond_init()
pthread_cond_signal()
pthread_cond_wait()

Semaphore and Condition Variable both are recommended to be used with mutexes for safety and locking features. Whenever your program performs an action that may change the sense of the condition
you’re protecting with the condition variable, it should perform these steps. (In
our example, the condition is the state of the thread flag, so these steps must be taken
whenever the flag is changed.)
1. Lock the mutex accompanying the condition variable.
2. Take the action that may change the sense of the condition (in our example, set the flag).
3. Signal or broadcast the condition variable, depending on the desired behavior.
4. Unlock the mutex accompanying the condition variable.

No comments:

Post a Comment