.NET:如何在BeginInvoke回调期间与窗体对话?

.NET:如何在BeginInvoke回调期间与窗体对话?,.net,asynchronous,delegates,.net,Asynchronous,Delegates,我有一个长时间运行的函数\: public string FindPasswordFromHash(String hash) { ... } 这被称为: private void Button1_Click(object sender, EventArgs e) { PasswordTextBox.Text = FindPasswordFromHash(HashTextBox.Text); } 现在我想将其转换为异步BeginInvoke/EndInvoke委托模式: pri

我有一个长时间运行的函数\:

public string FindPasswordFromHash(String hash)
{
    ...
}
这被称为:

private void Button1_Click(object sender, EventArgs e)
{
    PasswordTextBox.Text = FindPasswordFromHash(HashTextBox.Text);
}
现在我想将其转换为异步BeginInvoke/EndInvoke委托模式:

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);
当然,这不起作用,因为异步委托的实现方式存在漏洞:

“跨线程操作无效: 已访问控件“PasswordTextBox” 从一个线程而不是它所在的线程 已在上创建。”

既然异步委托模式是为了将长时间运行的操作转换为异步操作而发明的,那么使用BeginInvoke/EndInvoke替代同步调用的合适技术是什么

更具体地说,强制回调被封送回调用线程的方法是什么



例如发明的函数名

对于类似的东西,最好的选择是使用对象。后台工作对象将允许您运行任务并使用ReportProgress更新表单

希望这有帮助!
JFV

您正在正确处理BeginInvoke()和EndInvoke()调用。您只需要处理这样一个事实,即操作GUI需要在GUI线程上完成

幸运的是,框架提供了方法,允许您在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);


下面是另一个可能有助于解释引擎盖下发生了什么的实现

        string hash = " your hash text ";

    delegate string MyAsyncDelegate(String hash);

    delegate void UpdateDelegate(string pwd);

    private string FindHash(string hs) {
        Thread.Sleep(5000);

        return "hash computed by worker Thread: " + Thread.CurrentThread.ManagedThreadId;
    }

    private void Button1_Click(object sender, EventArgs e) {
        //invoke FindHash on another thread from the threadpool.
        MessageBox.Show("Current Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        MyAsyncDelegate asyncDelegate = new MyAsyncDelegate(this.FindHash);
        asyncDelegate.BeginInvoke(hash, RecoveryCompleteCallback, asyncDelegate);
    }

    private void RecoveryCompleteCallback(IAsyncResult result) {
        MyAsyncDelegate asyncDelegate = (MyAsyncDelegate)result.AsyncState;
        string pwd = asyncDelegate.EndInvoke(result);

        UpdatePassword(pwd);
    }

    private void UpdatePassword(string s) {

        System.ComponentModel.ISynchronizeInvoke invoker = PasswordTextBox as System.ComponentModel.ISynchronizeInvoke;
        if (invoker != null && invoker.InvokeRequired) {
            // still in worker thread.
            invoker.Invoke(new UpdateDelegate(UpdatePassword), new object[] { s });
        } else {
            PasswordTextBox.Text = s;
            MessageBox.Show("Current Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }
    }

我知道这有点冗长,但它可能有助于你理解。。。基本上,您永远不希望从GUI线程以外的线程更新GUI

我忘记了“invokererequired”和使用.Invoke()。但是这些语法是什么呢?是C#还是VB?它不是在VS2005中编译的。它是C#3.0。这里它只是作为匿名委托的简写。这是如何使用InvokeRequired等的一个很好的示例,但是手头的任务(在后台运行长任务)最好使用BackgroundWorker来处理。调用模式更适合于简单地跨越线程边界进行“快速”操作。后台工作程序增加了一些功能,例如,如果任务需要很长时间,可以轻松添加进度条。这可能是比使用BeginInvoke/EndInvoke更好的建议。考虑到工作限制,很难想象异步委托的实际用途。
        string hash = " your hash text ";

    delegate string MyAsyncDelegate(String hash);

    delegate void UpdateDelegate(string pwd);

    private string FindHash(string hs) {
        Thread.Sleep(5000);

        return "hash computed by worker Thread: " + Thread.CurrentThread.ManagedThreadId;
    }

    private void Button1_Click(object sender, EventArgs e) {
        //invoke FindHash on another thread from the threadpool.
        MessageBox.Show("Current Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        MyAsyncDelegate asyncDelegate = new MyAsyncDelegate(this.FindHash);
        asyncDelegate.BeginInvoke(hash, RecoveryCompleteCallback, asyncDelegate);
    }

    private void RecoveryCompleteCallback(IAsyncResult result) {
        MyAsyncDelegate asyncDelegate = (MyAsyncDelegate)result.AsyncState;
        string pwd = asyncDelegate.EndInvoke(result);

        UpdatePassword(pwd);
    }

    private void UpdatePassword(string s) {

        System.ComponentModel.ISynchronizeInvoke invoker = PasswordTextBox as System.ComponentModel.ISynchronizeInvoke;
        if (invoker != null && invoker.InvokeRequired) {
            // still in worker thread.
            invoker.Invoke(new UpdateDelegate(UpdatePassword), new object[] { s });
        } else {
            PasswordTextBox.Text = s;
            MessageBox.Show("Current Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }
    }