将UI Thread方法传递给另一个线程以便在C#中调用

时间:2017-12-05 11:00:57

标签: c# multithreading winforms invoke

我在C#中有一个Windows窗体项目。在这个项目中,有一个WaveOut设备可以在单独的线程中播放。该回放线程周期性地需要调用UI线程方法并向其传递一些数据(包含传递给声卡的音频信息的数组)。 passAudio方法定期调用连接的EventHandler。

现在,waveout设备(WaveOutPlayer.cs)有一个EventHandler:

public class WaveOutPlayer : IDisposable
{
    public event EventHandler<AudioEventArgs> BufferSwapped;
    ...
    private void passAudio(byte[] pAudiodata)
    {
        AudioEventArgs args = new AudioEventArgs();
        args.Data = pAudiodata;
        args.WaveFormat = ws.Format;
        if (BufferSwapped != null)
        {
            BufferSwapped.Invoke(this, args);
        }
    }
}

Windows窗体实例连接到此EventHandler:

private void Start()
{
    WaveStream Audio = new WaveStream("sine440hz_16bit_stereo.wav");
    WaveOutPlayer wp = new WaveOutPlayer(audio, 0);
    wp.BufferSize = 8192; // testing
    wp.Repeat = false; // 'true' not implemented yet
    wp.BufferSwapped += Wp_BufferSwapped;
}

private void Wp_BufferSwapped(object sender, AudioEventArgs e)
{
    // The audio buffer data can be found in the event args.
    // So analyze this Audio and manipulate some of the forms' controls 
    // accordingly.

    this.labelForAmplitude.Text = "some value";
}

然而,这导致异常,因为Wp_BufferSwapped-Method实际上属于回放线程,因此可能无法操纵标签的文本。

现在: 如何在不使Windows窗体代码更难的情况下解决此问题?这样做的原因是我想让我的学生(高中)用数组和简单的用户界面做一些很酷的事情。但此时他们只对用户界面工作有了非常基本的了解。他们对BeginInvoke或MethodInvoker之类的东西一无所知。 所以我想以DLL的形式给他们WaveOutPlayer - 他们只需要处理Windows窗体。 这种特殊问题有解决方案吗?

1 个答案:

答案 0 :(得分:1)

您可以在构造函数中捕获当前SynchronizationContext,然后Post将事件处理程序调用到它,如下所示:

public class WaveOutPlayer {
    private readonly SynchronizationContext _context;
    public WaveOutPlayer() {
        // capture
        _context = SynchronizationContext.Current;
    }

    public event EventHandler<AudioEventArgs> BufferSwapped;

    private void passAudio(byte[] pAudioData) {
        var args = new AudioEventArgs();
        args.Data = pAudioData;
        var handler = BufferSwapped;
        if (handler != null) {
            if (_context != null)
                // post
                _context.Post(_ => handler(this, args), null);
            else
                handler(this, args);
        }
    }
}

通过执行此操作,您不会将winforms的依赖性引入WaveOutPlayer,同时WinForms部分不需要编译操作,事件处理程序只是在UI线程上调用。请注意,Post此处将与Control.BeginInvoke类似。如果您想要模仿Control.Invoke - 请改用Send