12

我有两个线程,一个在紧密循环中工作,另一个偶尔需要与第一个执行同步:

// 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,即使在多核系统上也是如此?

处理此同步过程的最有效方法是什么?

4

5 回答 5

8

您可以在 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 互斥锁,从而允许其他线程加入队列。

于 2012-10-03T07:12:18.320 回答
5

在您的情况下,最好使用条件变量在需要唤醒并执行所有必需操作时通知第二个线程。

于 2012-10-02T06:13:37.160 回答
3

pthread在其 API 中提供了线程优先级的概念。当两个线程竞争一个互斥体时,调度策略会决定哪一个会得到它。该功能pthread_attr_setschedpolicy允许您设置它,并pthread_attr_getschedpolicy允许检索信息。

现在坏消息:

  • 当只有两个线程锁定/解锁互斥锁时,我看不到任何形式的竞争,第一个运行原子指令的人接受它,其他人阻止。我不确定此属性是否适用于此。
  • 该函数可以采用不同的参数(SCHED_FIFO, SCHED_RR,SCHED_OTHERSCHED_SPORADIC),但在这个问题中,已回答仅SCHED_OTHER在 linux 上受支持)

所以如果我是你,我会试一试,但不要期望太高。pthread_yield对我来说似乎更有希望。更多信息可在此处获得。

于 2012-10-02T06:36:48.450 回答
0

上面的票锁看起来是最好的。但是,为了确保您的 pthread_yield 正常工作,您可以等待 bool,它由 thread2 设置和重置。只要设置了 bool waiting,thread1 就会产生。

于 2013-09-16T14:19:12.290 回答
0

这是一个简单的解决方案,适用于您的情况(两个线程)。如果您正在使用,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;
};
于 2017-10-24T08:37:19.887 回答