2010-10-26 51 views
5

我是C#和多线程的新手,所以我很抱歉,如果这是一个重复的问题,但作为一个新手它看起来我的问题与我读过的其他人略有不同。C#多线程 - 更新与背景事件的GUI

我的GUI在一个(主)线程中运行。它调用一个后台任务(在一个dll中 - 我也在写),它运行在一个单独的线程中。 dll对GUI不了解(即不能引用GUI类)。

现在,假设我想根据dll线程的状态更新GUI上的进度条 - >我正在做的是在dll中创建一个事件,每X%会触发并且GUI将会订阅此事件。当事件被触发时,GUI将更新进度条。

我的问题:

  1. 是创建活动的最佳方式(铭记DLL不能引用GUI)的方法?
  2. 如何确保我的上述方法是“事件安全”?我是否应该在事件中通过进度百分比以保证线程安全或者是否还有更多内容?
  3. 更新GUI时是否需要使用Invoke?我看到一篇文章提示我做了,但我不明白为什么自从GUI线程完成了更新吧!

希望你能澄清我的这个!

谢谢

+0

“安全事件”是什么意思? – 2010-10-26 16:55:16

+0

请在此处查看“BackgroundWorker”类的注释:http://stackoverflow.com/questions/1506838/backgroundworker-vs-background-thread/1507337#1507337 – 2010-10-26 18:24:10

+0

我只想说感谢所有答复。他们都帮我回答了我的问题。很高兴看到这样的帮助! – SimpleOne 2010-10-26 19:34:03

回答

3

1 - 我用这种方法所有的时间和肯定它会工作

2,只需通过一个int值以事件处理程序和变量将是安全的阅读。然而,当你从代码fireing事件做这样的

private void UpdatePercentage(int a) 
{ 
    var myEvent = PercentageUpdatedEvent 
    if(myEvent != null) 
     myEvent(this, new ProgressBarEventArgs(a)); 
} 

这样做的原因是如此,如果事件被空支票和喊你不会得到一个例外之间unsubcribed。

3.-正如其他人所提到的,你将需要调用Invoke,因为事件将在dll的线程上运行。然而,通过控件,在没有EndEnvoike的情况下调用BeginInvoke是合法的,因此调用将不会在dll的线程中阻塞。

这是我一直使用

+0

这确实有帮助。非常感谢。它终于点击了(可能对你们都很明显),即使事件处理程序是在GUI类中编写的,它仍然在dll线程中运行,因为它是从那里'调用'的!现在我明白了为什么我们需要Invoke,并澄清了它。我同意其他职位,我需要了解为什么/如何工作,而不是使用BackgroundWorker等。 – SimpleOne 2010-10-26 19:32:59

3

看看BackgroundWorker类。这听起来很适合你的场景。

MSDN上此链接介绍如何使用它:http://msdn.microsoft.com/en-us/library/cc221403%28VS.95%29.aspx

+2

BackgroundWorker试图帮助您解决运行后台操作的任务,而不需要任何有关线程和UI如何工作的真实知识,但是如果确实了解事件如何引发以及如何使用Dispatcher,则无法提供多少帮助。我强烈建议任何想要做多线程代码的人至少试图理解抽象背景下的情况,比如后台工作者。 – SoftMemes 2010-10-26 17:14:00

+0

我同意重要的是要弄清楚发生了什么,但我认为BackgroundWorker可能是最安全的方式来实现它,如果你不明白发生了什么。 – Dismissile 2010-10-26 17:21:10

0

要回答(3),你将需要使用调用。事件处理程序将从后台线程运行,而不是GUI线程。

3

请记住,在大多数情况下,后台任务引发的事件也可以在后台线程上运行。根本没有线程上下文切换自动发生。

要理解为什么,你必须考虑事件是什么;只是某种类型的Delegate对象。您正在为主线程设置该事件的委托...但该委托实际上将在后台线程中触发事件的代码中调用。

所以是的;您需要确保您正在将事情移动到该事件处理程序内的GUI线程上运行。

+0

指出它:“但该代表实际上将在后台线程中,在触发事件的代码中调用。” – SimpleOne 2010-10-26 19:35:12

0

如果你分离一个线程,你需要创建一个委托,它可以安全地用适当的参数调用你的主线程。

delegate void UpdateDelegate(int val) 
void Update(int val) 
{ 
    if(this.InvokeRequired()) 
    { 
    Invoke(new UpdateDeleage(Update),new object[] {val}); 
    return; 
    } 
    this.MyProgressBar.Value = val; 
} 

从您的单独线程调用更新,就像您从主线程调用它一样。一旦线程确定您的主线程需要调用来传递该值,它将使用您的委托与您传递的参数一起调用它。否则,它会简单地跳过该块并设置您的值。

例如

... 

new Thread(()=>IncrementValues()).Start(); 

... 

void IncrementValues() 
{ 
    while(true) 
    Update(new Random(0,10)); 
} 
0

模式我有一个on my blog几种不同的方法解决这个问题,有各自的优势/劣势。总之,我建议使用Task类。