【问题标题】:Threading newbie with issue with runaway threads给新手处理线程失控的问题
【发布时间】:2010-08-11 23:34:14
【问题描述】:

好的,你明白我会解释这个问题: 我正在使用一个名为 ClanLIB 的库(不是我的选择),该库 SEEMLY(我不确定,甚至阅读源代码)创建了一个处理声音的线程。

这个线程,当缓冲区为空时,会尝试获取更多数据,这通常会在数据生成库太慢而无法在声卡到达缓冲区结束之前提供更多数据时导致欠载。

所以我添加了自己的线程,它不断在背景上产生声音。

这很好用,只是我自己的线程有时会占用过多的 CPU 时间并冻结其他所有内容。为了解决这个问题,我添加了条件等待。

条件等待发生在缓冲区已满时,当 ClanLIB 请求更多数据时,会发出等待信号,因此缓冲区写入线程会继续(直到再次满)。

我的问题是,由于我添加了这个条件等待、ClanLIB 声音线程和我自己的音乐线程,有时会“失控”,在应用程序的其余部分冻结时播放音乐。

什么样的奇怪情况会导致这种情况?

伪代码:

//main thread (that get frozen)
start_sound_thread();
do_lots_of_stuff();
quit();

//Sound Thread:

While(true)
{
    play(buffer);
    if(buffer_empty)
    {
         mutex.lock()
         buffer = buffer2;
         if(buffer2_full)
         {
             signal(cond1);
             buffer2_full = false;
         }
         mutex.unlock()
    }
}

//Music Library Thread:

while(true)
{
    mutex.lock()        
    if( check_free_space(buffer2) == 0)
    {
        buffer2_full = true;
        condition_wait(cond1);
    }
    write_music(buffer2);
    mutex.unlock()
}

【问题讨论】:

  • 我正在使用 pthreads,但我也在其他地方使用 Lua,而 Lua 使用 boost 线程(我不知道 boost 线程是如何工作的)
  • 会将 condition_wait(cond1) 放入循环帮助中,boost.org/doc/libs/1_41_0/doc/html/thread/…?
  • 我不这么认为... condition_wait 的工作原理是释放互斥锁,并等待“信号”警告它可以继续(然后它就像一个“lock_mutex()”并等待用于释放互斥锁)
  • 关于循环,我想到了这一点:en.wikipedia.org/wiki/Spurious_wakeup
  • 确实,虚假唤醒是可能的!现在我修复了代码,因此它们不会发生(即:再次检查缓冲区已满情况的循环)但问题仍然存在:(

标签: c++ multithreading pthreads buffering


【解决方案1】:

您在互斥体区域内做的太多了 - 您很可能在 cond1 信号上陷入僵局。你应该尽可能少地在锁定区域内做,因为你做的越多,你引入死锁的风险就越大 - 你绝对不应该等待保护信号发送的互斥锁内的信号 - 如果你'重新等待,它永远不会被发送 - condition_wait 没有解锁你的互斥对象,它怎么知道呢?

“通过添加条件等待来解决”是一个强有力的指标,您遇到了导致死锁的竞争条件 - 只要您的逻辑的执行时间因任何原因发生变化,您就没有解决问题,比如说因为另一个应用程序正在运行,死锁可以返回。

【讨论】:

    【解决方案2】:

    我也将 clanlib 用于项目,但使用 OpenAL 进行声音。它似乎与提供缓冲区等的工作方式相同。

    我也使用单独的线程来处理声音,但我的解决方案是在每次尝试输入缓冲区后添加一个固定的睡眠时间。它很简单,看起来很可靠。并且不需要锁定;)

    【讨论】:

      【解决方案3】:

      其实我也不知道是什么问题,是不小心修复的……

      我有一个相关的问题,这个问题是由删除线程时的竞争条件引起的……我移动了一些代码(我什至不需要重新编码任何东西),它也消失了。现在事情完全稳定了! (嗯,仍然存在不足,但现在更罕见了)

      【讨论】:

        【解决方案4】:

        如果您使用的是 ClanLib 游戏 SDK,它是一个经过完善、测试和支持的 SDK。无意冒犯,但是,由于您是公认的新手,因此问题更可能出现在您的代码中而不是他们的代码中。

        我很难相信这样一个完善的图书馆会发展不足,即使确实如此,我怀疑最好的解决方案是added my own thread, that keeps generating sound on the background。我这样说是因为我怀疑您采用了错误的方法,并且如果您可以使用 ClanLib 找到解决方案,那么您将不需要您的线程并且您的问题将消失而无需解决。

        很高兴您发布了代码,谢谢。我现在就去看看。

        最后,作为 SO 的忠实粉丝,先向官方 ClanLib forum 提问不是更好吗?


        编辑:是什么让你如此确定它是一个不足?我仍然觉得这很难相信,但如果你在你的程序中检测到它,那么为什么不sleep(),而不是产生声音呢? (睡多久?你怎么知道要产生多少声音?产生的声音不会干扰“真实”的声音吗?)

        【讨论】:

        • 我已经在那里问过了,他们不知道为什么他们的代码做得更好(如果您阅读源代码,您甚至会看到他们的缓冲区读取被硬编码为 1024*16 无论如何much 是播放频率,或通道,或每个样本有多少字节......)此外,它是 ClanLIB 1.0,当数据获取速度不够快时,它确实会导致欠载,并且它也没有预缓冲(它真的在等待缓冲区结束,我自己已经阅读了源代码以确保)
        • 你已经问过了。好(+1)。我很惊讶他们不准备修复它。不过,看起来你可以使用 if(buffer_empty) 来检测它,所以当你检测到它时,为什么还要添加更多的声音(听起来很奇怪)?为什么不只是 sleep() 而忘记第二个线程?您可以定期醒来并检查更多声音
        • 我无法检测到欠载,我听到欠载(以声卡的形式重复缓冲区的最后一部分,直到处理新数据),问题是 ClanLIB 只允许一点时间声音发生器产生声音,所以如果代码更重,它会运行不足......无论如何,我添加了一些条件等待......(我尝试了睡眠,但是当更重的音乐出现时,它们完全被打破了),似乎是解决了(似乎是因为我们永远不知道是否修复了一些错误,特别是在它们很少见的时候)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-20
        • 2021-12-01
        • 2010-10-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多