C# 从另一个线程在UI线程上运行方法
我有一个班,播放一些像这样的音乐。在构造期间,它还将GUI线程id保存在私有int中:C# 从另一个线程在UI线程上运行方法,c#,multithreading,C#,Multithreading,我有一个班,播放一些像这样的音乐。在构造期间,它还将GUI线程id保存在私有int中: public class MediaPlayer { public event EventHandler<Track> OnTrackComplete; private int GuiThreadId; public MediaPlayer(...){ ... this.GuiThreadId = Thread.CurrentThread.Man
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);
}
}
为了能够在另一个线程上执行代码,必须有一个队列或消息泵等待处理新项目 这已经在winforms和wpf中通过
Control.Invoke
和IDispatcher.Invoke
完成。如果您真的不想让控件
执行监听,则必须将该控件传递到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);});
}
}
公共类MediaPlayer{
公共事件事件处理程序OnTrackComplete;
私有int-id;
专用只读控制;
公共媒体播放器(…,控制){
...
this.GuiThreadId=Thread.CurrentThread.ManagedThreadId;
this.controll=控制;
}
公共游戏{
Task t=Task.Factory.StartNew(()=>
{
//歌曲全集
FireOnTrackComplete();
});
}
受保护的虚拟void FireOnTrackComplete()
{
var trackComplete=OnTrackComplete;
如果(onTrackComplete!=null)
调用((MethodInvoker)委托{trackComplete(this,loadedTrack);});
}
}
抱歉,如果有错别字,我面前没有任何东西可以核实;但这应该能让您了解您的目的。为了能够在另一个线程上执行代码,您必须有一个队列或消息泵等待处理新项目 这已经在winforms和wpf中通过
Control.Invoke
和IDispatcher.Invoke
完成。如果您真的不想让控件
执行监听,则必须将该控件传递到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);});
}
}
公共类MediaPlayer{
公共事件事件处理程序OnTrackComplete;
私有int-id;
专用只读控制;
公共媒体播放器(…,控制){
...
this.GuiThreadId=Thread.CurrentThread.ManagedThreadId;
this.controll=控制;
}
公共游戏{
Task t=Task.Factory.StartNew(()=>
{
//歌曲全集
FireOnTrackComplete();
});
}
受保护的虚拟void FireOnTrackComplete()
{
var trackComplete=OnTrackComplete;
如果(onTrackComplete!=null)
调用((MethodInvoker)委托{trackComplete(this,loadedTrack);});
}
}
抱歉,如果有错别字,我面前没有任何东西可以核实;但这会让你得到你想要的东西。本书旨在解决这个问题。在构造函数中复制其当前属性的值,稍后使用其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;
}
这个计划的目的是解决这个问题。在构造函数中复制其当前属性的值,稍后使用其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线程的调度程序),并使用回调代码直接对其调用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.
// ...
公共类MediaPlayer{
公共事件事件处理程序OnTrackComplete;
专用调度程序{get;set;}
公共媒体播放器(调度程序){
//其他代码。。。
if(guiDispatcher==null)
抛出新ArgumentNullException(“guiDispatcher”,“无法正确初始化媒体播放器,因为无法在GUI线程上触发回调。”);
调度员=guiDispatcher;
}
公共游戏{
//在调用“Play”的线程上立即触发,因为我们将转发gui线程上的exec。
FireOnTrackComplete();
}
受保护的虚拟void FireOnTrackComplete()
{
//假装“loadedTrack”是在以前的某个地方设置的。
Dispatcher.Invoke(()=>{
如果(OnTrackComplete!=null)
OnTrackComplete(此为加载的Track);
});
}
}
//在初始化代码中的某个地方
// ...
MediaPlayer=新的MediaPlayer(App.Current.Dispatcher);//如果你使用WPF。不知道这是否也适用于WinForms。
// ...
将引用传递给主调度程序(=GUI线程的调度程序),并使用回调代码直接调用调用
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.
// ...
公共类MediaPlayer{
公共事件事件处理程序OnTrackComplete;
专用调度程序{get;set;}
公共媒体播放器(调度程序){
//其他代码。。。
if(guiDispatcher==null)
抛出新ArgumentNullException(“guiDispatcher”,“无法正确初始化媒体播放器,因为无法在GUI线程上触发回调。”);
调度员=guiDispatcher;
}
公共游戏{
//在调用“Play”的线程上立即触发,因为我们将转发gui线程上的exec。
FireOnTrackComplete();
}
受保护的虚拟void FireOnTrackComplete()
{
//假装“loadedTrack”是在以前的某个地方设置的。
Dispatcher.Invoke(()=>{
如果(OnTrackComplete!=null)
OnTrackComplete(此为加载的Track);
});
}
}
//在初始化代码中的某个地方
// ...
MediaPlayer=新的MediaPlayer(App.Current.Dispatcher);//如果你使用WPF。不知道这是否也适用于WinForms。
// ...
我建议立即存储对主(=GUI)调度器的引用,而不是冗余地存储threadId和控件。这样您就可以简单地执行:\u guiDispatcher.Invoke(()=>{FireOnTrackComplete();})代码>@Jay是的我可以这么做但是