我正在 Xamarin 中编写一个应用程序。我正在使用 Azure 的 TTS 服务。我有两种不同的方法。两种方法都使用 TTS。但我不希望其中一个在另一个完成之前使用它。我该怎么做?
这是主法师:
namespace PorjectB1.Controller
{
public partial class MainPage : ContentPage
{
private System.Timers.Timer m1Timer;
private System.Timers.Timer m2Timer;
static Model.TextToSpeech ttsService = new Model.TextToSpeech();
public MainPage()
{
InitializeComponent();
m1Timer= new System.Timers.Timer(15000);
m1Timer.Elapsed += OnM1TimerElapsed;
m1Timer.AutoReset = true;
m1Timer.Enabled = true;
m2Timer = new System.Timers.Timer(60000);
m2Timer.Elapsed += OnM2Elapsed;
m2Timer.AutoReset = true;
m2Timer.Enabled = true;
}
private void OnM1TimerElapsed(object sender, ElapsedEventArgs e)
{
M1(this);
}
private void OnM2Elapsed(object sender, ElapsedEventArgs e)
{
M2(this);
}
private async void M1(object sender, EventArgs e)
{
string SpeechText=string.Empty;
//Do Something...
Speak(SpeechText);
}
private async void M2(object sender, EventArgs e)
{
string SpeechText=string.Empty;
//Do Something...
Speak(SpeechText);
}
private void Speak(string text)
{
ttsService.Speak(text);
}
}
}
这是 TTS 服务类:
using Microsoft.CognitiveServices.Speech;
namespace PorjectB1.Model
{
public class TextToSpeech
{
static private string speechKey { get; set; }
static private string speechRegion { get; set; }
public TextToSpeech()
{
speechKey = "***";
speechRegion = "***";
}
static void OutputSpeechSynthesisResult(SpeechSynthesisResult speechSynthesisResult, string text)
{
switch (speechSynthesisResult.Reason)
{
case ResultReason.SynthesizingAudioCompleted:
//Log Something
break;
case ResultReason.Canceled:
//Log Something
break;
default:
//Log Something
break;
}
}
public async void Speak(string text)
{
var speechConfig = SpeechConfig.FromSubscription(speechKey, speechRegion);
using (var speechSynthesizer = new SpeechSynthesizer(speechConfig))
{
var speechSynthesisResult = await speechSynthesizer.SpeakTextAsync(text);
OutputSpeechSynthesisResult(speechSynthesisResult, text);
}
}
}
}
我在主页 Speak 方法上尝试了 Lock、Monitor 和 SemaphorSlim 来解决这个问题。
为了确保 Xamarin 应用程序中的一个文本转语音 (TTS) 方法不会在另一个方法完成之前启动,使用 SemaphoreSlim 等同步技术是一种很好的方法。您似乎已经尝试过使用 Lock、Monitor 和 SemaphoreSlim,但让我们根据您的用例使用 SemaphoreSlim 改进实现。
SemaphoreSlim 非常适合这种情况,因为它提供了一种等待异步操作的方法,同时限制可以同时访问资源或资源池的线程数量。
以下是修改 MainPage 和 TextToSpeech 类以使用 SemaphoreSlim 的方法:
定义一个 SemaphoreSlim:在 MainPage 类中,定义一个初始计数为 1 的 SemaphoreSlim。这意味着一次只有一个线程可以进入临界区。
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
修改 Speak 方法:在 TextToSpeech 类的 Speak 方法中,使用 semaphoreSlim.WaitAsync() 和 semaphoreSlim.Release() 包装 TTS 逻辑以确保同步。
public class TextToSpeech
{
// ... existing fields and constructor ...
public async Task Speak(string text)
{
await semaphoreSlim.WaitAsync();
try
{
var speechConfig = SpeechConfig.FromSubscription(speechKey, speechRegion);
using (var speechSynthesizer = new SpeechSynthesizer(speechConfig))
{
var speechSynthesisResult = await speechSynthesizer.SpeakTextAsync(text);
OutputSpeechSynthesisResult(speechSynthesisResult, text);
}
}
finally
{
semaphoreSlim.Release();
}
}
}
将 SemaphoreSlim 传递给 TTS 服务:将 semaphoreSlim 实例传递给 TextToSpeech 服务,以便它可以使用它进行同步。
static Model.TextToSpeech ttsService = new Model.TextToSpeech(semaphoreSlim);
调整TextToSpeech中的构造函数:修改TextToSpeech的构造函数以接受SemaphoreSlim参数。
public TextToSpeech(SemaphoreSlim semaphore)
{
speechKey = "***";
speechRegion = "***";
this.semaphoreSlim = semaphore;
}
通过这些更改,您的 TTS 方法将同步 - 当 M1 调用 TTS 时,M2 将等待 M1 完成后再开始,反之亦然。这可确保两种方法不会同时调用 TTS 服务。