pthreads:快速重新锁定导致的线程不足
pthreads: thread starvation caused by quick re-locking
我有两个线程,一个在紧密循环中工作,另一个偶尔需要与第一个线程执行同步:
// 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);
}
我的目的是线程 2 可以通过锁定有效地暂停线程 1 并执行必要的同步。线程 1 还可以通过解锁提供暂停,如果线程 2 未等待锁定,则重新锁定并恢复工作。
我遇到的问题是互斥体不公平,所以线程 1 会迅速重新锁定互斥锁并饿死线程 2。我尝试使用 pthread_yield
,到目前为止它似乎运行良好,但我不确定它是否适用于所有系统/内核数。有没有办法保证线程 1 将始终屈服于线程 2,即使在多核系统上也是如此?
处理此同步过程的最有效方法是什么?
您可以在 pthreads 互斥锁之上构建一个 FIFO"票证锁",如下所示:
#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);
}
在这种方案下,当一个线程在票证锁保护的关键部分内时,不会持有低级 pthreads 互斥锁,允许其他线程加入队列。
在您的情况下,最好使用条件变量在需要唤醒和执行所有必需操作时通知第二个线程。
pthread
在其API中提供了线程优先级的概念。当两个线程竞争互斥锁时,调度策略将确定哪一个线程将获得它。函数pthread_attr_setschedpolicy
允许您设置,并且pthread_attr_getschedpolicy
允许检索信息。
现在坏消息:
- 当只有两个线程锁定/解锁互斥锁时,我看不到任何形式的竞争,第一个运行原子指令的人接受它,其他块。我不确定此属性是否适用于此处。
- 该函数可以采用不同的参数(
SCHED_FIFO
,SCHED_RR
,SCHED_OTHER
和SCHED_SPORADIC
),但是在这个问题中,已经回答了Linux仅支持SCHED_OTHER
)
所以如果我是你,我会试一试,但不要期望太高。 对我来说,pthread_yield
似乎更有希望。更多信息可在此处获得。
票证锁看起来是最好的。 但是,为了确保您的pthread_yield正常工作,您可以等待一个布尔值,该布尔值由 thread2 设置和重置。 只要设置了布尔等待,thread1 就会产生。
这是一个简单的解决方案,适用于您的情况(两个线程)。如果您使用的是std::mutex
那么此类是直接替换。将互斥锁更改为此类型,可以保证如果一个线程持有锁,而另一个线程正在等待锁,一旦第一个线程解锁,第二个线程将在第一个线程再次锁定之前抓住锁。
如果两个以上的线程碰巧同时使用互斥锁,它仍然可以运行,但不能保证公平性。
如果您使用的是普通pthread_mutex_t
则可以根据此示例轻松更改锁定代码(解锁保持不变)。
#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;
};
- 如何检查线程是否锁定
- 在两个线程上读/写 64 位,无互斥/锁定/原子
- 如何在实时应用程序中锁定线程
- 锁定来自其他线程的类成员
- 当只有一个线程主要使用该对象而其他线程很少使用它时,如何最小化该对象的互斥锁锁定?
- 将线程锁定很长时间
- C++将互斥锁锁定为来自另一个线程
- 在以读取为主的多线程程序中,可以使用原子来减少锁定吗
- std::weak_ptr<T>::锁定线程安全吗?
- 线程锁定互斥锁的速度比 std::conditional_variable::wait() 快
- C 优化导致线程锁定
- 是否可以在C++中使用std::atomic_flag获得线程锁定机制
- mutex::lock() 检查一次解锁状态是否已经被另一个线程锁定?
- C++11中的基本线程锁定
- boost线程锁定错误
- 当一个线程锁定一个大映射时,如何避免冻结其他线程
- 导致c++ 11 std::互斥锁将被阻塞的线程锁定到被动等待状态
- shared_ptr.get()可以被多个线程调用,而另一个线程锁定并调用shared_ptr.swap()吗?
- 发现可能由于线程锁定(可能)导致的性能问题
- C++:如何简化线程锁定