我对使用基于 pthreads 和 Ubuntu 开发环境的线程的最新 gcc 中的互斥锁和消息传递的性能感兴趣。一个很好的通用问题是哲学家就餐,每个哲学家使用左手和右手邻居共享的 lh 和 rh 叉子。我将哲学家的数量增加到 99 以保持我的四核处理器忙碌。
int result = try_lock(forks[lhf], forks[rhf]);
上面的代码允许我的哲学家尝试抓住他们需要用来吃饭的两个叉子。
// if the forks are locked then start eating
if (result == -1)
{
state[j] = philosophers::State::Eating;
eating[j]++;
if (longestWait < waiting[j])
{
longestWait = waiting[j];
}
waiting[j] = 0;
} else {
state[j] = philosophers::State::Thinking;
thinking[j]++;
waiting[j]++;
}
上面的代码监控我的哲学家的进食或思考进度,这取决于他们是否设法保留了两个叉子。
{
testEnd te(eating[j]+thinking[j]-1);
unique_lock<mutex> lk(cycleDone);
endCycle.wait(lk, te);
}
上述代码等待所有哲学家完成选择,此时哲学家可以自由地进行新的尝试:
if ( philosophers::State::Eating == state[j] )
{
state[j] = philosophers::State::Thinking;
forks[lhf].unlock();
forks[rhf].unlock();
}
我有一个主线程来监控哲学家并将他们从一个周期移动到下一个周期,让他们有大约 10 秒的时间尽可能多地吃饭和思考。结果是大约 9540 个循环,一些哲学家挨饿,而另一些则有充足的食物和大量的思考时间!所以我需要保护我的哲学家免于饥饿和等待太久,所以我添加了更多的逻辑来防止暴饮暴食,要求吃的哲学家在很短的休息后释放和思考,而不是用同样的叉子说话:
// protect the philosopher against starvation
if (State::Thinking == previous)
{
result = try_lock(forks[lhf], forks[rhf]);
}
现在我有 9598 个周期,每个哲学家都获得相对平等的进食份额(2620 - 2681),并且思考时间最长为 14。还不错。但是我并不满意,所以现在我摆脱了所有互斥锁和锁,并保持简单,偶数哲学家在偶数周期中进食,奇数哲学家在奇数周期中进食。我使用一种简单的方法来同步哲学家
while (counter < counters[j])
{
this_thread::yield();
}
使用全局循环计数器防止哲学家进食或思考太多次。同一时间段,哲学家管理大约 73543 个周期,其中 36400 次进食,等待的周期不超过 3 个。所以我没有锁的简单算法速度更快,并且在各个线程之间有更好的处理分布。
谁能想到更好的方法来解决这个问题?我担心当我实现一个具有多个线程的复杂系统时,如果我遵循传统的互斥锁和消息传递技术,我最终会在系统中的各个线程上得到比必要的慢和可能的不平衡处理。