我有一个事件和它的方法声明如下,这是一个windows窗体登录控件的身份验证事件:
public event EventHandler<AuthenticateEventArgs> Authenticate;
protected void OnAuthenticate(AuthenticateEventArgs e)
{
EventHandler<AuthenticateEventArgs> handler = Authenticate;
if (handler != null)
{
handler(this, e);
}
if (e.Authenticated)
{
OnLoggedIn(new EventArgs());
}
else
{
OnLoggedError(new EventArgs());
}
}
单击按钮引发事件,现在假设在其他项目中有此事件的订阅者,如下所示:
this.loginControl1.Authenticate += loginControl1_Authenticate;
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
};
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000);
ea.Authenticated = true;
};
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
};
System.Threading.Thread.Sleep(2000);
只是一些需要一些时间的过程的模拟。问题是,最后一个订阅者执行OnAuthenticate
方法中的If条件并引发另一个事件,而之前的订阅者没有。代码完全适用于一个订阅者。在这种情况下问题出在哪里?
由于您希望异步执行您的身份验证方法,您可以这样做。
创建一个返回bool
的委托
public delegate bool Authenticate(object sender, AuthenticateEventArgs e);
Authenticate authHandler;
你可以使用或不使用这些参数,但你可以在以后使用或删除它们。
创建您的认证方法
bool AuthenticationMethod1(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000); //Simulate some long running task.
return false; //Return true or false based on authentication failed or succeeded.
}
bool AuthenticationMethod2(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000); //Simulate some long running task.
return true; //Return true or false based on authentication failed or succeeded.
}
bool AuthenticationMethod3(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000); //Simulate some long running task.
return false; //Return true or false based on authentication failed or succeeded.
}
连接处理程序
authHandler += AuthenticationMethod1;
authHandler += AuthenticationMethod2;
authHandler += AuthenticationMethod3;
现在执行if (authHandler != null)
{
foreach (Authenticate handler in authHandler.GetInvocationList())
{
handler.BeginInvoke(this, e as AuthenticateEventArgs, new AsyncCallback(Callback), handler);
}
}
最后一部分:你定义了回调吗
void Callback(IAsyncResult ar)
{
Authenticate d = (Authenticate)ar.AsyncState;
if (d.EndInvoke(ar))
{
OnLoggedIn(new EventArgs());
}
else
{
OnLoggedError(new EventArgs());
}
}
按此代码删除事件:
this.loginControl1.Authenticate -= loginControl1_Authenticate;
问题是所有的事件订阅者都将与触发它们的函数在同一个线程上执行,所以当执行到达
handler(this, e);
执行将移动到第一个订阅者代码
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
,然后执行转到第二个订阅者。最后,它将执行最后一个订阅者代码,然后执行返回到调用者函数
protected void OnAuthenticate(AuthenticateEventArgs e)
,从事件触发行继续执行到第二个if语句,此时ea.Authenticated的值为false,因为上次订阅者设置了它。
如果你想在不同的线程中触发每个事件,检查触发事件在分开的线程
这是众所周知的行为,事件按照订阅的顺序执行,因此最后订阅的订阅者将覆盖之前的所有值。您可以看到最后一个订阅者的更新(在本例中为false)。
您可以通过检查已验证并跳过处理来修复此问题。
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
if(ea.Authenticated)
{
return;
}
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
};
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
if(ea.Authenticated)
{
return;
}
System.Threading.Thread.Sleep(2000);
ea.Authenticated = true;
};
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
if(ea.Authenticated)
{
return;
}
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
};
原因,因为你看到Authenticated
false总是handler(this, e);
只会在调用所有订阅的方法后返回。因此,您的最后一个订阅者将Authenticated
设置为false,因此只有在执行if (e.Authenticated)
时才会看到false。