使用异步Task和Textbox.Text =“ Hello”时出现问题

时间:2018-07-19 07:57:08

标签: c# async-await google-cloud-speech

首先,对不起,因为我在C#领域还很陌生,所以我决定提出这个问题,因为我已经被这个问题困扰了几个小时。

我有一个可与Google Cloud Speech服务一起使用并进行语音转文本操作的GUI。我与您分享单击按钮时运行的整个方法:

private async Task<object> StreamingMicRecognizeAsync(int seconds)
    {
        if (NAudio.Wave.WaveIn.DeviceCount < 1)
        {
            Console.WriteLine("No microphone!");
            return -1;
        }

        GoogleCredential googleCredential;
        using (Stream m = new FileStream(@"..\..\credentials.json", FileMode.Open))
            googleCredential = GoogleCredential.FromStream(m);
        var channel = new Grpc.Core.Channel(SpeechClient.DefaultEndpoint.Host,
            googleCredential.ToChannelCredentials());
        var speech = SpeechClient.Create(channel);

        var streamingCall = speech.StreamingRecognize();

        // Write the initial request with the config.
        await streamingCall.WriteAsync(
            new StreamingRecognizeRequest()
            {
                StreamingConfig = new StreamingRecognitionConfig()
                {
                    Config = new RecognitionConfig()
                    {
                        Encoding =
                        RecognitionConfig.Types.AudioEncoding.Linear16,
                        SampleRateHertz = 48000,
                        LanguageCode = "es-ES",
                    },
                    InterimResults = true,
                }
            });

        // Read from the microphone and stream to API.
        object writeLock = new object();
        bool writeMore = true;
        var waveIn = new NAudio.Wave.WaveInEvent();
        waveIn.DeviceNumber = 0;
        waveIn.WaveFormat = new NAudio.Wave.WaveFormat(48000, 1);
        waveIn.DataAvailable +=
            (object sender, NAudio.Wave.WaveInEventArgs args) =>
            {
                lock (writeLock)
                {
                    if (!writeMore) return;
                    streamingCall.WriteAsync(
                        new StreamingRecognizeRequest()
                        {
                            AudioContent = Google.Protobuf.ByteString
                                .CopyFrom(args.Buffer, 0, args.BytesRecorded)
                        }).Wait();
                }
            };

        // Print responses as they arrive.
        Task printResponses = Task.Run(async () =>
        {
            while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
            {
                foreach (var result in streamingCall.ResponseStream
                    .Current.Results)
                {
                    foreach (var alternative in result.Alternatives)
                    {
                        Console.WriteLine(alternative.Transcript);
                        //Textbox1.Text = alternative.Transcript;
                    }
                }
            }
        });

        waveIn.StartRecording();
        Console.WriteLine("Speak now.");
        Result_Tone.Text = "Speak now:\n\n";
        await Task.Delay(TimeSpan.FromSeconds(seconds));

        // Stop recording and shut down.
        waveIn.StopRecording();
        lock (writeLock) writeMore = false;
        await streamingCall.WriteCompleteAsync();
        await printResponses;
        return 0;
    }

我的问题是我想更新Textbox1控件的内容,但是它不起作用。它将输出完美地用行Console.WriteLine(alternative.Transcript);写到控制台中,而不写到我的文本框中。

如果有人可以帮助我,我将非常感谢他的帮助。

3 个答案:

答案 0 :(得分:5)

问题是您正在使用Task.Run,这意味着您的代码将在线程池线程上运行。

除了调用Task.Run()之外,只需将代码移到单独的异步方法中即可:

async Task DisplayResponses(IAsyncEnumerator<StreamingRecognizeResponse> responses)
{
    while (await responses.MoveNext(default(CancellationToken)))
    {
        foreach (var result in responses.Current.Results)
        {
            foreach (var alternative in result.Alternatives)
            {
                Textbox1.Text = alternative.Transcript;
            }
        }
    }
}

然后从UI线程上已存在的代码(例如事件处理程序)中直接调用该方法 (无Task.Run)。

异步机制将确保在await表达式之后,您返回到UI线程(相同的同步上下文)。因此,对Text属性的分配将在UI线程上进行,并且一切都会很好。

例如:

// This would be registered as the event handler for a button
void HandleButtonClick(object sender, EventArgs e)
{
     var stream = client.StreamingRecognize();
     // Send the initial config request
     await stream.WriteAsync(...);

     // Presumably you want to send audio data...
     StartSendingAudioData(stream);

     await DisplayResponses(stream.ResponseStream);
}

答案 1 :(得分:2)

任务在单独的线程上运行,因此您必须Invoke在控件的线程上执行的操作

Textbox1.Invoke(new Action(() =>
{
    Textbox1.Text= "";
}));

编辑:对于WPF,我相信等效值为

Textbox1.Dispatcher.Invoke(new Action(() =>
{
    Textbox1.Text= "";
}));

答案 2 :(得分:0)

您是否尝试过使用Dispatcher.InvokeASync()?

await Dispatcher.InvokeAsync(() => {while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
        {
            foreach (var result in streamingCall.ResponseStream
                .Current.Results)
            {
                foreach (var alternative in result.Alternatives)
                {                       
                    Textbox1.Text = alternative.Transcript;
                }
            }
        }});