【问题标题】:Pthread Mutex: pthread_mutex_unlock() consumes lots of timePthread Mutex:pthread_mutex_unlock() 消耗大量时间
【发布时间】:2013-05-29 03:37:09
【问题描述】:

我使用生产者-消费者模型用 pthread 编写了一个多线程程序。

当我使用英特尔 VTune 分析器分析我的程序时,我发现生产者和消费者在 pthread_mutex_unlock 上花费了大量时间。我不明白为什么会这样。我认为线程可能要等待很长时间才能获得互斥锁,但释放互斥锁应该很快,对吧?

下面的快照来自英特尔 VTune。它显示了消费者尝试从缓冲区中获取项目的代码,以及每个代码行所消耗的时间。

我的问题是为什么 pthread_mutex_unlock 有这样的开销?是 pthread mutex 本身的问题还是我使用它的方式有问题?

【问题讨论】:

  • 如果互斥锁上存在大量争用,解锁该互斥锁可能会很慢,因为解锁的部分工作是唤醒等待互斥锁的所有线程。
  • 我认为如果将pthread_mutex_unlock() 调用移到pthread_cond_signal() 调用上方,看看结果会很有趣。在向条件变量发出信号时不需要保持互斥锁(仅在等待它时),我怀疑发生的情况是信号导致互斥锁争用,因为立即释放的线程尝试获取互斥锁,而信号线程仍然存在。
  • @MichaelBurr 好点!我根据您的建议进行了测试,该程序现在快了大约 40%。
  • @stone199141:感谢您让我们知道结果。如果不是太麻烦,我有兴趣在将更改添加到问题后获得来自 vtune 的等效屏幕截图(或者我想它应该在答案中)。
  • @MichaelBurr:在他的代码中进行这样的更改并不安全!如果他在持有互斥锁的同时发出信号,则保证该信号会唤醒一个选择阻塞在完整状态的线程。如果他在释放互斥锁后发出信号,则另一个线程可能会阻塞处于空状态的条件变量,并且他可能会在发出信号时唤醒该线程。该线程将重新进入睡眠状态,信号将丢失。如果您使用相同的条件变量来发出多个状态的信号,则在发出信号之前您无法安全地解锁互斥锁。 (你可以在广播之前。)

标签: multithreading pthreads mutex intel-vtune


【解决方案1】:

pthread_mutex_unlock() 函数应该释放由 mutex 引用的 mutex 对象。但是,释放互斥锁的方式取决于互斥锁的类型属性。如果在调用pthread_mutex_unlock()时mutex引用的mutex对象上有线程阻塞,导致mutex可用,调度策略将决定哪个线程获取mutex。

如果互斥体类型为PTHREAD_MUTEX_NORMAL,则不应提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果一个线程试图解锁一个它尚未锁定的互斥锁或一个已解锁的互斥锁,则会导致未定义的行为。

如果互斥体类型为PTHREAD_MUTEX_ERRORCHECK,则应提供错误检查。如果线程试图重新锁定它已经锁定的互斥锁,则应返回错误。如果一个线程试图解锁一个它没有锁定的互斥锁或者一个解锁的互斥锁,将会返回一个错误。

如果互斥锁类型是PTHREAD_MUTEX_RECURSIVE,那么互斥锁应该维护锁计数的概念。当线程第一次成功获取互斥锁时,锁计数应设置为 1。每次线程重新锁定此互斥体时,锁定计数应加一。每次线程解锁互斥锁时,锁计数应减一。当锁计数达到零时,互斥锁将可供其他线程获取。如果一个线程试图解锁一个它没有锁定的互斥锁或者一个解锁的互斥锁,将会返回一个错误。

如果互斥锁类型是PTHREAD_MUTEX_DEFAULT,尝试递归锁定互斥锁会导致未定义的行为。如果调用线程未锁定互斥锁,则尝试解锁互斥锁会导致未定义的行为。如果互斥锁未锁定,则尝试解锁它会导致未定义的行为。

我通常更喜欢使用PTHREAD_MUTEX_RECURSIVE 互斥体,因为在这种情况下,当计数达到零并且调用线程不再对该互斥体拥有任何锁时,互斥体将变得可用。

【讨论】:

    猜你喜欢
    • 2014-03-03
    • 1970-01-01
    • 2012-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    相关资源
    最近更新 更多