我有一个长时间运行的函数¹:
public string FindPasswordFromHash(String hash)
{
...
}
它被称为:
private void Button1_Click(object sender, EventArgs e)
{
PasswordTextBox.Text = FindPasswordFromHash(HashTextBox.Text);
}
现在我想将它转换成异步的 BeginInvoke/EndInvoke 委托(delegate)模式:
private void Button1_Click(object sender, EventArgs e)
{
MyAsyncDelegate asyncDelegate = new MyAsyncDelegate(HashTextBox.Text);
asyncDelegte.BeginInvoke(hash, CompleteCallback, null);
}
private void CompleteCallback(IAsyncResult ar)
{
MyAsyncDelegate asyncDelegate = ((AsyncResult)ar).AsyncDelegate;
PasswordTextBox.Text = asyncDelegate.EndInvoke(asyncResult);
}
delegate string MyAsyncDelegate(String hash);
当然这是行不通的,因为异步委托(delegate)的实现方式存在抽象漏洞:
"Cross-thread operation not valid: Control 'PasswordTextBox' accessed from a thread other than the thread it was created on."
鉴于发明异步委托(delegate)模式是为了将长时间运行的操作转换为异步操作 - 使用 BeginInvoke/EndInvoke 替代同步调用的正确技术是什么?
更具体地说,强制将回调编码回调用线程的方法是什么?
¹ 例如发明的函数名称
最佳答案
您正在正确处理 BeginInvoke() 和 EndInvoke() 调用。您只需要处理这样一个事实:操作 GUI 需要在 GUI 线程上完成。
幸运的是,框架提供了 Control.Invoke()方法,它允许您在 GUI 线程上执行代码。
我通常会这样做:
private void SetPasswordText(string password){
if(InvokeRequired){
MethodInvoker mi = () => SetPasswordText(password);
Invoke(mi);
return;
}
PasswordTextBox.Text = password;
}
对于这种特殊情况,您也可以这样做
private void RecoveryCompleteCallback(IAsyncResult ar)
{
MyAsyncDelegate asyncDelegate = ((AsyncResult)ar).AsyncDelegate;
string password = asyncDelegate.EndInvoke(asyncResult);
Invoke(()=>{PasswordTextBox.Text = password;});
}
如果你使用 C# 2.0,你会这样做:
MethodInvoker mi = delegate(){ SetPasswordText(password); };
Invoke(mi);
或
Invoke(delegate(){PasswordTextBox.Text = password;});
关于.NET:如何在 BeginInvoke 回调期间与表单对话?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/508386/