17

Ich habe eine asp.net MVC-Anwendung, die eine Controlleraktion hat, die eine Zeichenfolge als Eingabe verwendet und eine Antwort-WAV-Datei der synthetisierten Sprache sendet. Hier ist ein vereinfachtes Beispiel:System.Speech.Synthes hängt mit hoher CPU auf 2012 R2

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; 
    } 

Die Anwendung (und diese Aktion Verfahren insbesondere) läuft fein in einer Server-Umgebung auf 2008 R2 Server 2012 (nicht-R2) Server und meinen 8.1 dev PC. Es läuft auch gut auf einer standardmäßigen virtuellen Azure 2012 R2-Maschine. Wenn ich sie jedoch auf drei 2012 R2-Servern (deren endgültiges Heim) bereitstellen, erzeugt die Aktionsmethode niemals eine HTTP-Antwort - der IIS-Arbeitsprozess maximiert einen der CPU-Kerne auf unbestimmte Zeit. Es ist nichts in der Ereignisanzeige und nichts springt auf mich heraus, wenn ich den Server mit Procmon beobachte. Ich habe den Prozess mit Remote-Debugging angefügt und die synth.Speak(text) kehrt nie zurück. Wenn der Aufruf synth.Speak(text) ausgeführt wird, sehe ich sofort den Runaway-Prozess w3wp.exe im Task-Manager des Servers.

Meine erste Neigung war auf den Servern einige Verfahren wurden stören Sprachsynthese im Allgemeinen zu glauben, aber die Windows-Erzähler richtig funktioniert, und eine einfache Konsolenanwendung wie das funktioniert auch richtig:

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

So offensichtlich Ich kann die Sprachsynthese des Servers im Allgemeinen nicht tadeln. Vielleicht gibt es ein Problem in meinem Code oder etwas Seltsames in der IIS-Konfiguration? Wie kann ich diese Controller-Aktion auf diesen Servern korrekt ausführen?

Dies ist eine einfache Möglichkeit, die Aktion-Methode zu testen (nur die url Wert direkt für das Routing bekommen müssen):

<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> 
+0

Haben Sie versucht, eine synchrone Aktionsmethode zu erstellen, ohne sie in eine Aufgabe zu integrieren? Es könnte Probleme mit dem Thread-Pool in diesem Code geben, von dem ASP .NET nichts weiß. –

+0

@DmitryS. Ich bin mir nicht sicher, ob ich einen solchen Test machen kann ... wenn 'synth.Speak' nicht umgebrochen ist, bekomme ich eine Laufzeitausnahme:' InvalidOperationException' (Ein asynchroner Vorgang kann zu diesem Zeitpunkt nicht gestartet werden). Ich mag die Denkweise, aber wenn es ein Thread-Pool-Problem mit ASP.NET wäre, warum würde es auf so vielen anderen Servern funktionieren? – hmqcnoesy

+0

Die neueste Version des Synthesizers hat die 'SpeakAsync()' Methode. Anstatt den Code in 'Task.Run()' einzufügen, können Sie '' SpeakAsync (Text) '' einfach warten. https://msdn.microsoft.com/en-us/library/system.speech.synthesis.speechsynthesizer.speakasync%28v=vs.110%29.aspx –

Antwort

1

Ich habe festgestellt, dass ich das Problem auf anderen Servern, einschließlich Azure-VMs, reproduzieren kann. Daher schloss ich die Möglichkeit eines Problems mit unserer speziellen Umgebung aus.

Auch ich fand, dass ich den Code für 2012 R2 gut funktionieren konnte, wenn ich den Anwendungspool unter einer Identität ausgeführt, die ein Administrator auf dem Server war und zuvor auf dem Server angemeldet war. Nach einem sehr langen Prozess zum Ausschließen von Berechtigungsproblemen entschied ich, dass es etwas im Anmeldeprozess sein muss, dass die TTS-API-Aufrufe korrekt funktionieren. (Was auch immer es ist, ich konnte es nicht finden, indem ich procmon Spuren durchbohrte). Glücklicherweise kann die ApplicationPoolIdentity ähnliche Anmeldemagie haben, indem Sie "Erweiterte Einstellungen" für den App-Pool in IIS öffnen und Load User Profile auf True setzen.

Die Identität, die den app-Pool betreibt, muss auch die Erlaubnis HKU\.Default\Software\Microsoft\Speech zu lesen, die unter Verwendung des lokalen Servers für den Standort und IIS APPPOOL\.Net v4.5 für die Benutzername zu Application gewährt werden können (wo .Net v4.5 der Name des Anwendungspools ist).

Sobald die Leseberechtigung für den Registrierungsschlüssel erteilt wurde und der App-Pool zum Laden des Benutzerprofils konfiguriert ist, funktioniert der obige Code einwandfrei. Getestet auf Azure VMs und Vanille 2012 R2 von MSDN ISOs.

1

Ich denke, die Frage der Art Rückkehr ist. IIS Express ist so dass Sie weg mit ihm, aber IIS ist nicht:

Task<FileContentResult> 

Also, wenn Sie versuchen:

public async Task<FileContentResult> 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; 
} 

Ich wette, Sie auch das Audio/wav MIME-Typen in IIS hinzufügen müssen.

+0

Leider, nein, das ändert nichts am Verhalten von 2012 R2. – hmqcnoesy

+0

Der MIME-Typ funktioniert auch nicht? –

+0

Nein, die WAV-MIME-Typen wurden bereits in IIS konfiguriert. Aber das sind sowieso statische Dateien. – hmqcnoesy

-1

Dies ist nur aus der Spitze von meinem Kopf und es wurde nicht getestet, aber Sie können etwas sein können, wie dies zu tun:

public ActionResult Speak(string text) 
{ 
var speech = new SpeechSynthesizer(); 
speech.Speak(text); 

byte[] bytes; 
using (var stream = new MemoryStream()) 
{ 
    speech.SetOutputToWaveStream(stream); 
    bytes = stream.ToArray(); 
} 
return File(bytes, "audio/x-wav"); 
} 
1

ich diese Erfahrung mit Server gehabt haben 2012R2 vor (nicht die Synth-API gewährt, aber dasselbe Problem). Ich habe es mit "await task.ConfigureAwait (false)" bei allen meinen Aufgaben behoben. Sehen Sie, ob das für Sie funktioniert.

Viel Glück.

+0

Danke, aber leider nein, das hat nicht geholfen, dieses spezielle Problem zu lösen. – hmqcnoesy

1

Bei this blog können Sie eine Lösung für ein ähnliches Problem finden - Ausnahme bei der Verwendung von SpeechSynthesizer auf frische Windows 8.1-Installation. In diesem Fall liegt das Problem im falschen Berechtigungseintrag für den CurrentUserLexicon-Benutzer (der von SpeechSynthesizer verwendet wird. Zur Lösung schlägt dieser Blogeintrag vor, den Berechtigungseintrag "ALLE APPLIKATIONSPAKETE" aus dem Registrierungsschlüssel Software \ Microsoft \ Speech \ CurrentUserLexicon zu entfernen.