繁体   English   中英

从另一个线程在 UI 线程上运行方法

[英]Run method on UI thread from another thread

我有一个 class 可以播放这样的音乐。 它还在构造过程中将 GUI 线程 id 保存在私有 int 中:

public class MediaPlayer {

    public event EventHandler<Track> OnTrackComplete;
    private int GuiThreadId;

    public MediaPlayer(...){
      ...
      this.GuiThreadId = Thread.CurrentThread.ManagedThreadId;
    }

    public void Play(){
        Task t = Task.Factory.StartNew(() =>
        {

            //On Song complete
            FireOnTrackComplete();
        });
    }

    protected virtual void FireOnTrackComplete()
    {
        if (OnTrackComplete != null)
            OnTrackComplete(this, loadedTrack);
    }
}

是否可以在具有特定 ID 的线程上调用FireOnTrackComplete() 在这种情况下,ID 存储在this.GuiThreadId中?

我遇到的大多数解决方案都建议我在我的 GUI 代码中在侦听OnTrackComplete事件处理程序的方法中使用调用。 我想避免这样做。 我想在MediaPlayer class 中做所有事情


根据下面接受的答案,这就是我更改代码的方式

public class MediaPlayer { public event EventHandler<Track> OnTrackComplete; private SynchronizationContext callerCtx; public MediaPlayer(...){... callerCtx = System.Threading.SynchronizationContext.Current; } public void Play(){ Task t = Task.Factory.StartNew(() => { //On Song complete FireOnTrackComplete(); }); } protected virtual void FireOnTrackComplete() { Action e = () => { if (OnTrackComplete,= null) OnTrackComplete(this; loadedTrack); }; FireEvent(e). } //... Other events..; // protected virtual void FireEvent(Action e) { if (callerCtx == null) e(). else callerCtx,Post(new SendOrPostCallback((_) => e()); null); } }

SynchronizationContext类用于解决此问题。 将其Current属性的值复制到构造函数中,稍后使用其Post()或Send()方法。 这样可以确保您的库可以与任何GUI类库一起使用。 像这样:

class MediaPlayer {
    public MediaPlayer() {
        callersCtx = System.Threading.SynchronizationContext.Current;
        //...
    }

    private void FireOnTrackComplete() {
        if (callersCtx == null) FireOnTrackCompleteImpl();
        else callersCtx.Post(new System.Threading.SendOrPostCallback((_) => FireOnTrackCompleteImpl()), null);
    }

    protected virtual void FireOnTrackCompleteImpl() {
        var handler = OnTrackComplete;
        if (handler != null) handler(this, loadedTrack);
    }

    private System.Threading.SynchronizationContext callersCtx;
}

将引用传递给主调度程序(= GUI-Thread的调度程序),并直接使用回调代码对其调用Invoke。

public class MediaPlayer {

    public event EventHandler<Track> OnTrackComplete;
    private Dispatcher { get; set; }

    public MediaPlayer(Dispatcher guiDispatcher){
        // Other code ...
        if(guiDispatcher == null) 
            throw new ArgumentNullException("guiDispatcher", "Cannot properly initialize media player, since no callback can be fired on GUI thread.");
        Dispatcher = guiDispatcher;
    }

    public void Play() {
        // Fire immediately on thread calling 'Play', since we'll forward exec. on gui thread anyway.
        FireOnTrackComplete(); 
    }

    protected virtual void FireOnTrackComplete()
    {
        // Pretending "loadedTrack" was set somewhere before.
        Dispatcher.Invoke(() => {
            if (OnTrackComplete != null)
                OnTrackComplete(this, loadedTrack);
        });
    }
}
// Somewhere in your initialization code
// ...
MediaPlayer player = new MediaPlayer(App.Current.Dispatcher); // If you use WPF. Don't know if this applies to WinForms too.
// ...

为了能够在另一个线程上执行代码,必须有一个队列或消息泵正在等待新项目的处理。

这已经在Winforms和wpf中通过Control.InvokeIDispatcher.Invoke 如果您确实想避免让Control执行监听,则必须将控件传递给MediaPlayer 这确实很尴尬,但是对此有一个很大的抱怨,那就是第一个答案是“您如何停止做您想做的事情”。

public class MediaPlayer {

    public event EventHandler<Track> OnTrackComplete;
    private int GuiThreadId;
    private readonly Control control;

    public MediaPlayer(..., Control control){
      ...
      this.GuiThreadId = Thread.CurrentThread.ManagedThreadId;
      this.contrl = control;
    }

    public void Play(){
        Task t = Task.Factory.StartNew(() =>
        {

            //On Song complete
            FireOnTrackComplete();
        });
    }

    protected virtual void FireOnTrackComplete()
    {
        var trackComplete = OnTrackComplete;
        if (onTrackComplete != null)
            this.control.Invoke((MethodInvoker) delegate {trackComplete(this, loadedTrack);});
    }
}

抱歉,如果有错字,我没有什么要验证的。 但这应该可以为您带来帮助。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM