System.Speech.Synthesis在2012 R2上挂起了高CPU

hmq*_*esy 17 c# asp.net-mvc text-to-speech speech-synthesis windows-server-2012-r2

我有一个asp.net MVC应用程序,它有一个控制器动作,它接受一个字符串作为输入,并发送一个合成语音的响应wav文件.这是一个简化的例子:

    public async Task<ActionResult> Speak(string text)
    {
        Task<FileContentResult> task = Task.Run(() =>
        {
            using (var synth = new System.Speech.Synthesis.SpeechSynthesizer())
            using (var stream = new MemoryStream())
            {
                synth.SetOutputToWaveStream(stream);
                synth.Speak(text);
                var bytes = stream.GetBuffer();
                return File(bytes, "audio/x-wav");
            }
        });
        return await task;
    }
Run Code Online (Sandbox Code Playgroud)

应用程序(特别是此操作方法)在2008 R2服务器,2012(非R2)服务器和我的8.1 ​​dev PC上的服务器环境中运行良好.它在标准的Azure 2012 R2虚拟机上运行良好.但是,当我将其部署到三个2012 R2服务器(最终的永久主页)时,操作方法永远不会产生HTTP响应 - IIS工作进程无限期地最大化其中一个CPU核心.事件查看器中没有任何内容,并且在使用Procmon查看服务器时没有任何内容跳出来.我已经通过远程调试附加到进程,并且synth.Speak(text)永远不会返回.当synth.Speak(text)执行呼叫我立即看到在服务器的任务管理器失控W3wp.exe进程.

我的第一个倾向是相信一些过程在服务器上干扰语音合成,但是Windows讲述者工作正常,像这样的简单控制台应用程序也能正常工作:

static void Main(string[] args)
{
    var synth = new System.Speech.Synthesis.SpeechSynthesizer();
    synth.Speak("hello");
}
Run Code Online (Sandbox Code Playgroud)

所以显然我不能责怪服务器的语音合成.那么也许我的代码中存在问题,或者IIS配置中有什么奇怪的东西?如何在这些服务器上使此控制器操作正常工作?

这是测试操作方法的简单方法(只需url为路由获取正确的值):

<div>
    <input type="text" id="txt" autofocus />
    <button type="button" id="btn">Speak</button>
</div>

<script>
    document.getElementById('btn').addEventListener('click', function () {
        var text = document.getElementById('txt').value;
        var url = window.location.href + '/speak?text=' + encodeURIComponent(text);
        var audio = document.createElement('audio');
        var canPlayWavFileInAudioElement = audio.canPlayType('audio/wav'); 
        var bgSound = document.createElement('bgsound');
        bgSound.src = url;
        var canPlayBgSoundElement = bgSound.getAttribute('src');

        if (canPlayWavFileInAudioElement) {
            // probably Firefox and Chrome
            audio.setAttribute('src', url);
            audio.setAttribute('autoplay', '');
            document.getElementsByTagName('body')[0].appendChild(audio);
        } else if (canPlayBgSoundElement) {
            // internet explorer
            document.getElementsByTagName('body')[0].appendChild(bgSound);
        } else {
            alert('This browser probably can\'t play a wav file');
        }
    });
</script>
Run Code Online (Sandbox Code Playgroud)

hmq*_*esy 2

我发现我可以在其他服务器(包括 Azure VM)上重现该问题,因此我排除了我们特定环境出现问题的可能性。

另外,我发现如果我以服务器上的管理员身份运行应用程序池并且之前已登录到服务器,那么我可以让代码在 2012 R2 上正常工作。经过很长的排除权限问题的过程后,我认为一定是登录过程中发生的某些事情使 TTS API 调用能够正常工作。(无论它是什么,我无法通过 procmon 痕迹挖掘找到它)。Load User Profile幸运的是,通过在 IIS 中打开应用程序池的“高级设置”并将其设置为.ApplicationPoolIdentity 可以应用类似的登录魔法True

运行应用程序池的身份还需要读取权限,可以通过使用本地服务器的位置和用户名(其中是应用程序池的名称)HKU\.Default\Software\Microsoft\Speech将其授予 ApplicationPoolIdentity 。IIS APPPOOL\.Net v4.5.Net v4.5

一旦授予了 reg 密钥的读取权限,并且应用程序池配置为加载用户配置文件,上述代码就可以正常工作。在 Azure VM 和来自 MSDN ISO 的 vanilla 2012 R2 上进行了测试。