当前位置: 首页 > 知识库问答 >
问题:

c11中线程的性能

贲永思
2023-03-14

我对最新gcc中基于pthread和Ubuntu开发环境的线程的互斥锁和消息传递的性能感兴趣。一个很好的通用问题是用餐哲学家,每个哲学家使用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个周期,一些哲学家挨饿,另一些人有充足的食物和大量的思考时间!因此,我需要保护我的哲学家免受饥饿和等待太久的伤害,因此我增加了更多的逻辑,通过要求饮食哲学家在一个很小的Rest后释放和思考,而不是喋喋不休地用同样的叉子:

    // 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个。因此,我的无锁简单算法既更快,又在各种线程之间有更好的处理分布。

有人能想到更好的方法来解决这个问题吗?我担心当我实现一个多线程的复杂系统时,如果我遵循传统的互斥锁和消息传递技术,我最终会在系统中的各个线程上得到比必要的更慢和可能的不平衡处理。

共有1个答案

松思源
2023-03-14

这是探索c中线程问题的有趣方法。

为了解决具体问题:

我担心,当我实现一个具有多线程的复杂系统时,如果我遵循传统的互斥和消息传递技术,我的系统中的各个线程的处理速度会比必要的慢,并且可能会不平衡。

不幸的是,我能给你的最好答案是,这是一种有充分理由的恐惧。然而,调度和同步的成本对于应用程序来说是非常特殊的——这在设计大型系统时成为一个工程决策。首先,调度是NP难的(http://en.wikipedia.org/wiki/Multiprocessor_scheduling)但是有很好的近似值。

就你的例子而言,我认为根据你给出的结果很难得出一般性结论——有一个主要的要点:粗粒度同步和细粒度同步之间的权衡。这是一个研究得很好的问题,一些研究可能会有所帮助(例如http://ieeexplore.ieee.org/xpls/abs_all.jsp?arnumber=744377

总的来说,这涉及到一个工程问题,它将是特定于您想要解决的问题的,操作系统和硬件。

 类似资料:
  • 我在Ubuntu13.04桌面上运行这个非常简单的程序,但是如果我注释掉sleep_for一行,它会在从main打印cout后挂起。有人能解释为什么吗?据我所知,main是一个线程,t是另一个线程,在本例中,互斥体管理共享cout对象的同步。

  • 如何从线程池中找到60%(或N%)的线程可用性?这背后的逻辑是什么? 父线程使用线程池线程生成多个网址,并等待所有子线程完成。 代码如下所示 父线程 子线程 用于跨线程通信的对象数据 在上述代码中,所需的线程硬编码为: 这种硬编码会导致线程池不足吗?如果线程池中没有可用的线程,会发生什么?如何在托管服务器的线程池中查找可用线程的总数? 谢谢。

  • 根据C11,内的最后一行无效。为什么会这样?

  • 我明白为什么C 11中的类型提高了正确性和可运维性。我读到它也可以提高性能(赫伯·萨特的《几乎总是自动》),但我错过了一个很好的解释。 如何提高性能

  • 我正在的上的上运行应用程序。对于那些不熟悉的人,每当发生SQS事件时,Amazon都会自动向我的Worker应用程序执行请求。我的应用程序然后处理请求并返回200(除非明显出错)。 我想在处理单个请求时使用并发。例如,假设我需要执行10种不同的昂贵计算。我希望使用让10个线程同时执行这项工作,而不是串行执行。 这是坏习惯吗?如果没有,以这种方式启动新线程是否存在性能危险 线程的分配实际上是如何工作