C#多线程-使用后台事件更新GUI
我是C#和多线程的新手,所以如果这是一个重复的问题,我很抱歉,但作为一个新手,我的问题似乎与我读过的其他问题略有不同 我的GUI在一个(主)线程中运行。它调用一个在单独线程中运行的后台任务(在dll中,我也在编写)。dll不了解GUI(即,它不能引用GUI类) 现在,假设我想根据dll线程的状态更新GUI上的进度条->我正在dll中创建一个事件,每X%触发一次,GUI将订阅该事件。触发事件时,GUI将更新进度条 我的问题是:C#多线程-使用后台事件更新GUI,c#,multithreading,C#,Multithreading,我是C#和多线程的新手,所以如果这是一个重复的问题,我很抱歉,但作为一个新手,我的问题似乎与我读过的其他问题略有不同 我的GUI在一个(主)线程中运行。它调用一个在单独线程中运行的后台任务(在dll中,我也在编写)。dll不了解GUI(即,它不能引用GUI类) 现在,假设我想根据dll线程的状态更新GUI上的进度条->我正在dll中创建一个事件,每X%触发一次,GUI将订阅该事件。触发事件时,GUI将更新进度条 我的问题是: 创建事件的方法是最好的方法吗(请记住dll不能引用GUI) 我如何确保
谢谢查看BackgroundWorker课程。听起来很适合你的场景
MSDN上的此链接解释了如何使用它:要回答(3),您需要使用Invoke。事件处理程序将从后台线程而不是GUI线程运行。请记住,在大多数情况下,从后台任务引发的事件也将在后台线程上运行。根本不会自动进行线程上下文切换 要理解为什么,你必须考虑一个事件是什么;只是某种类型的
委托
对象。您正在从主线程设置该事件的委托。。。但该委托实际上将在后台线程中调用,在触发事件的代码中
是的;您需要确保将事件从事件处理程序中转移到GUI线程上运行。如果您派生线程,则需要创建一个委托,该委托可以使用适当的参数安全地调用主线程
delegate void UpdateDelegate(int val)
void Update(int val)
{
if(this.InvokeRequired())
{
Invoke(new UpdateDeleage(Update),new object[] {val});
return;
}
this.MyProgressBar.Value = val;
}
从单独的线程调用Update,就像从主线程调用Update一样。一旦线程确定需要调用主线程来传递值,它将使用您传递的参数和委托来调用它。否则,它将跳过该块并设置您的值
e、 g
1.-我一直使用这种方法,是的,它会起作用 2.-只需将int传递给事件处理程序,即可安全读取变量。但是,当您从代码触发事件时,请执行以下操作
private void UpdatePercentage(int a)
{
var myEvent = PercentageUpdatedEvent
if(myEvent != null)
myEvent(this, new ProgressBarEventArgs(a));
}
这样做的原因是,如果在空检查和调用之间取消预订事件,则不会出现异常
3.-正如其他人提到的,您需要调用Invoke,因为事件将在dll的线程上运行。但是,对于控件,在没有EndEnvoike的情况下调用BeginInvoke是合法的,因此调用在dll的线程上是非阻塞的
这是我经常使用的模式
private myClass_OnPercentageUpdatedEvent(object a, ProgressBarEventArgs e)
{
if(progressBar.InvokeRequired)
progressBar.BeginInvoke((Action<object,ProgressBarEventArgs>)myCless_OnPercentageUpdatedEvent, a, e);
else
{
progressBar.Value = e.Value;
}
}
private myClass\u onPercentageUpdateEvent(对象a,ProgressBarEventArgs e)
{
if(progressBar.invokererequired)
progressBar.BeginInvoke((操作)myCless_on PercentageUpdateEvent,a,e);
其他的
{
progressBar.Value=e.Value;
}
}
对于这个问题,我有几种不同的方法,每种方法都有各自的优缺点。总之,我建议使用任务
类。你所说的“事件安全”是什么意思?请参阅BackgroundWorker
类的评论:我只想感谢所有的回复。他们都帮助回答了我的问题。很高兴看到这样的帮助!BackgroundWorker试图帮助您解决运行后台操作的任务,而不需要任何关于线程和UI如何工作的实际知识,但如果您确实了解如何引发事件以及如何使用Dispatcher,则不会提供太多帮助。我强烈建议任何想做多线程代码的人,至少要尝试去理解在一个抽象(比如后台工作者)的框架下发生的事情。我同意理解框架下发生的事情是很重要的,但我认为,如果你不了解引擎盖下发生的事情,那么BackgroundWorker可能是实现它的最安全的方法。这真的很有帮助。非常感谢。它最后点击(可能对你们所有人来说都很明显),即使事件处理程序是在GUI类中编写的,它仍然在dll线程中运行,就像它被“调用”的地方一样!现在我明白了为什么我们需要调用,这就澄清了这一点。我同意其他帖子的观点,我需要理解它为什么/如何工作,而不是使用BackgroundWorker等。这些帖子明确了这一点:“但实际上,在触发事件的代码中,该委托将在后台线程中被调用。”
private myClass_OnPercentageUpdatedEvent(object a, ProgressBarEventArgs e)
{
if(progressBar.InvokeRequired)
progressBar.BeginInvoke((Action<object,ProgressBarEventArgs>)myCless_OnPercentageUpdatedEvent, a, e);
else
{
progressBar.Value = e.Value;
}
}