我怎么做事件回到我赢形式线的安全吗?
-
08-06-2019 - |
题
当你订个事件上的一个对象从内的一种形式,你基本上是移交控制你回调方法的事件源。你不知道这是否是事件源将选择触发事件在不同的线。
问题是,当回被援引,不能假设你可以做更新控制在您的形式,因为有时这些控制措施将把一个异常事件的回被称为一线不同于该线的形式运行。
解决方案
为了简化西蒙的代码一点,可以使用建立在通用行动代表。它节省了尖刻你的代码有一堆的委托类型你并不真正需要。此外,中。净3.5他们加入一个params参数的调用的方法,所以你不需要定义一个临时阵列。
void SomethingHappened(object sender, EventArgs ea)
{
if (InvokeRequired)
{
Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
return;
}
textBox1.Text = "Something happened";
}
其他提示
这里的要点:
- 你不能让UI控制的呼吁不同的线比一个他们创造了在(形式的螺纹)。
- 委托调用(即,事件挂钩)触发在同一线作为对象射击的事件。
所以,如果你有一个单独的"引擎"的线做一些工作,并有一些UI看态的变化可以反映在UI(例如取得进展酒吧或什么的),您有一个问题。发动机火的对象改变了事件已经上瘾的形式。但回调委托的形式注册的机会呼吁发动机的线...不上形成的螺纹。所以你不可以更新的任何控制,从那回调。Doh!
BeginInvoke 来拯救。只是用这种简单的编码模型在所有你回调的方法,你可以确信的事情都会好起来的:
private delegate void EventArgsDelegate(object sender, EventArgs ea);
void SomethingHappened(object sender, EventArgs ea)
{
//
// Make sure this callback is on the correct thread
//
if (this.InvokeRequired)
{
this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
return;
}
//
// Do something with the event such as update a control
//
textBox1.Text = "Something happened";
}
它真的很简单.
- 使用 InvokeRequired 找到了,如果这回事上的正确的线。
- 如果没有,那么reinvoke回调的正确的线与相同的参数。你可以reinvoke一方法通过使用 调用 (封锁)或 BeginInvoke (non-blocking)的方法。
- 下一次的功能是所谓, InvokeRequired 返回错误的,因为我们现在的正确的线和每个人都是幸福的。
这是一个非常紧凑的方式解决这个问题,并使您的形式从安全的多线的事件的回调。
我使用匿名方法很多,在这种情况:
void SomethingHappened(object sender, EventArgs ea)
{
MethodInvoker del = delegate{ textBox1.Text = "Something happened"; };
InvokeRequired ? Invoke( del ) : del();
}
我有点迟到这个话题,但是你可能想看一看 基于事件的异的图案.当正确实施,它保证,事件总是引起UI线。
这里有一个简单的例子是只允许一个并行的调用;支持多的调用/活动需要更多一点的管道。
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class MainForm : Form
{
private TypeWithAsync _type;
[STAThread()]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MainForm());
}
public MainForm()
{
_type = new TypeWithAsync();
_type.DoSomethingCompleted += DoSomethingCompleted;
var panel = new FlowLayoutPanel() { Dock = DockStyle.Fill };
var btn = new Button() { Text = "Synchronous" };
btn.Click += SyncClick;
panel.Controls.Add(btn);
btn = new Button { Text = "Asynchronous" };
btn.Click += AsyncClick;
panel.Controls.Add(btn);
Controls.Add(panel);
}
private void SyncClick(object sender, EventArgs e)
{
int value = _type.DoSomething();
MessageBox.Show(string.Format("DoSomething() returned {0}.", value));
}
private void AsyncClick(object sender, EventArgs e)
{
_type.DoSomethingAsync();
}
private void DoSomethingCompleted(object sender, DoSomethingCompletedEventArgs e)
{
MessageBox.Show(string.Format("DoSomethingAsync() returned {0}.", e.Value));
}
}
class TypeWithAsync
{
private AsyncOperation _operation;
// synchronous version of method
public int DoSomething()
{
Thread.Sleep(5000);
return 27;
}
// async version of method
public void DoSomethingAsync()
{
if (_operation != null)
{
throw new InvalidOperationException("An async operation is already running.");
}
_operation = AsyncOperationManager.CreateOperation(null);
ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
}
// wrapper used by async method to call sync version of method, matches WaitCallback so it
// can be queued by the thread pool
private void DoSomethingAsyncCore(object state)
{
int returnValue = DoSomething();
var e = new DoSomethingCompletedEventArgs(returnValue);
_operation.PostOperationCompleted(RaiseDoSomethingCompleted, e);
}
// wrapper used so async method can raise the event; matches SendOrPostCallback
private void RaiseDoSomethingCompleted(object args)
{
OnDoSomethingCompleted((DoSomethingCompletedEventArgs)args);
}
private void OnDoSomethingCompleted(DoSomethingCompletedEventArgs e)
{
var handler = DoSomethingCompleted;
if (handler != null) { handler(this, e); }
}
public EventHandler<DoSomethingCompletedEventArgs> DoSomethingCompleted;
}
public class DoSomethingCompletedEventArgs : EventArgs
{
private int _value;
public DoSomethingCompletedEventArgs(int value)
: base()
{
_value = value;
}
public int Value
{
get { return _value; }
}
}
}
作为 lazy programmer
, 我有一个很懒惰的方法这样做。
我做什么只是这样。
private void DoInvoke(MethodInvoker del) {
if (InvokeRequired) {
Invoke(del);
} else {
del();
}
}
//example of how to call it
private void tUpdateLabel(ToolStripStatusLabel lbl, String val) {
DoInvoke(delegate { lbl.Text = val; });
}
你可以联的DoInvoke内部的功能或者把它藏在单独的功能做肮脏的工作。
只要记住你可以通过直接进入功能DoInvoke方法。
private void directPass() {
DoInvoke(this.directInvoke);
}
private void directInvoke() {
textLabel.Text = "Directly passed.";
}
在许多简单的情况下,可以使用的MethodInvoker代表和避免需要创建你自己的委托类型。