Pthreads: starvation caused by quick re-blocking

I have two threads, one of which works in a narrow loop, and the other, which sometimes needs to synchronize with the first:

// thread 1 while(1) { lock(work); // perform work unlock(work); } // thread 2 while(1) { // unrelated work that takes a while lock(work); // synchronizing step unlock(work); } 

My intention is that thread 2 can, by locking, effectively suspend thread 1 and perform the necessary synchronization. Topic 1 may also suggest pausing by unlocking, and if thread 2 is not waiting for a lock, lock again and return to work.

The problem I ran into is that the mutexes are not fair, so thread 1 quickly blocks the mutex and head stream 2. I tried using pthread_yield and so far everything is working fine, but I'm not sure that it will work for all systems / number of cores. Is there a way to ensure that thread 1 will always give thread 2, even in multi-core systems?

What is the most efficient way to handle this synchronization process?

+8
c ++ multithreading pthreads
source share
5 answers

You can create a FIFO ticket lock on top of pthreads mutexes in the following lines:

 #include <pthread.h> typedef struct ticket_lock { pthread_cond_t cond; pthread_mutex_t mutex; unsigned long queue_head, queue_tail; } ticket_lock_t; #define TICKET_LOCK_INITIALIZER { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER } void ticket_lock(ticket_lock_t *ticket) { unsigned long queue_me; pthread_mutex_lock(&ticket->mutex); queue_me = ticket->queue_tail++; while (queue_me != ticket->queue_head) { pthread_cond_wait(&ticket->cond, &ticket->mutex); } pthread_mutex_unlock(&ticket->mutex); } void ticket_unlock(ticket_lock_t *ticket) { pthread_mutex_lock(&ticket->mutex); ticket->queue_head++; pthread_cond_broadcast(&ticket->cond); pthread_mutex_unlock(&ticket->mutex); } 

Under such a scheme, mutex pthreads of a low level are not executed anywhere when a thread is in a critical section protected by a ticket, allowing other threads to join the queue.

+3
source share

In your case, it is better to use a condition variable to notify the second thread when you need to wake up and perform all the necessary operations.

+5
source share

pthread offers the concept of thread priority in its API. When two threads compete with a mutex, the scheduling policy determines which one will be received. The pthread_attr_setschedpolicy function allows you to set this, and pthread_attr_getschedpolicy allows you to get information.

Now the bad news:

  • When only two threads lock / unlock the mutex, I don’t see any competitors, the first one who executes the atomic instruction takes it, the rest of the blocks. I am not sure if this attribute is applicable here.
  • The function can take different parameters ( SCHED_FIFO , SCHED_RR , SCHED_OTHER and SCHED_SPORADIC ), but in this question , the answer was given that only SCHED_OTHER supported in linux)

Thus, I would give him a chance if I were you, but do not expect too much. pthread_yield seems to me more promising. More information is available here .

+2
source share

Blocking tickets above looks like the best. However, to make pthread_yield work, you might have a bool wait that is installed and reset by thread2. thread1 gives as long as bool waiting is set.

0
source share

Here is a simple solution that will work for your case (two threads). If you use std::mutex , then this class is a replacement. Change your mutex to this type, and you are guaranteed that if one thread holds the lock and the other waits for it, then after the first thread is unlocked, the second thread will capture the lock before the first thread can lock it again.

If more than two threads use the mutex at the same time, it will still function, but there is no guarantee of justice.

If you use simple pthread_mutex_t , you can easily change your lock code according to this example (unlock remains unchanged).

 #include <mutex> // Behaves the same as std::mutex but guarantees fairness as long as // up to two threads are using (holding/waiting on) it. // When one thread unlocks the mutex while another is waiting on it, // the other is guaranteed to run before the first thread can lock it again. class FairDualMutex : public std::mutex { public: void lock() { _fairness_mutex.lock(); std::mutex::lock(); _fairness_mutex.unlock(); } private: std::mutex _fairness_mutex; }; 
0
source share

All Articles