为什么我的BeginInvoke方法不是异步的?

8
为了避免GUI冻结,我想异步运行连接到数据库的方法。因此,我编写了以下代码:
DelegatLoginu dl = ConnectDB;

IAsyncResult ar = dl.BeginInvoke(null, null);

var result = (bool)dl.EndInvoke(ar);

但是它仍然冻结,我不明白为什么。我认为BeginInvoke确保调用的代码在另一个线程中运行。谢谢!
7个回答

12

调用 EndInvoke() 方法将会阻塞当前线程直到 BeginInvoke() 调用完成。

如果你的长时间运行的方法需要在结束时回调,你需要使用这种模式:

public void DemoCallback()
{
    MethodDelegate dlgt = new MethodDelegate (this.LongRunningMethod) ;
    string s ;
    int iExecThread;

    // Create the callback delegate.
    AsyncCallback cb = new AsyncCallback(MyAsyncCallback);

    // Initiate the Asynchronous call passing in the callback delegate
    // and the delegate object used to initiate the call.
    IAsyncResult ar = dlgt.BeginInvoke(3000, out iExecThread, cb, dlgt); 
}

public void MyAsyncCallback(IAsyncResult ar)
{
    string s ;
    int iExecThread ;

    // Because you passed your original delegate in the asyncState parameter
    // of the Begin call, you can get it back here to complete the call.
    MethodDelegate dlgt = (MethodDelegate) ar.AsyncState;

    // Complete the call.
    s = dlgt.EndInvoke (out iExecThread, ar) ;

    MessageBox.Show (string.Format ("The delegate call returned the string:   \"{0}\", 
                                and the number {1}", s, iExecThread.ToString() ) );
}

我需要使用AsyncCallBack类还是可以传递简单的委托? - Petr
1
它必须是一个AsyncCallBack委托,也就是说,你的函数必须像上面的MyAsyncCallback()示例一样 - 返回void,并以IAsyncResult作为参数。 - RickL
我已经在本地测试了这段代码,但它无法正常工作(虽然编译通过,但屏幕上没有任何显示):http://ideone.com/V8b2NY - InfZero

5
请参见EndInvoke的描述这里,特别是以下内容:

EndInvoke()函数用于检索异步调用的结果。它可以在BeginInvoke()之后的任何时候调用。如果异步调用尚未完成,则EndInvoke()会阻塞直到其完成。


2

当调用dl.EndInvoke(ar)时,您立即阻塞了UI线程。这种做法有点违背异步调用的初衷。


好吧,这只是我的猜测……那么这有什么用呢?我原本以为它会在后台处理所有的连接并返回结果的 :( - Petr
BeginXxx 在调用后立即返回,此时连接处理正在后台进行。当结果准备好时,您将收到通知(通过回调或轮询 WaitHandle 中的 IAsyncResult 返回的方式)。此时,您调用 EndXxx 来检索结果(或获取在调用中发生的异常)。提前调用 EndXxx 强制等待结果准备就绪。 - Lucero

1

在.NET中使用异步模型有4种不同的模式,这个问题很好地解决了这个问题。

你正在使用“我会打电话给你”的方法。但是,如果你想等到工作项完成,最好的技术是使用MutexWaitHandle):

void Run()
{
    Action<string> doWork = DoWork;
    IAsyncResult result = doWork.BeginInvoke("I will call you", null, null);

    // You "call the method" - wait 10 seconds for the method to finish.
    bool success = result.AsyncWaitHandle.WaitOne(10 * 1000);
}

void DoWork()
{
}

我猜你不想阻塞,这种情况下“发射并忘记”是最不麻烦的选择。


0

在BeginInvoke中指定一个方法,在调用完成后该方法会被调用(例如dl.BeginInvoke(null, OnConnectCompleted))。这样线程就不会被阻塞。


0

0

调用 EndInvoke 将会阻塞当前线程。你应该将一个委托传递给 BeginInvoke 而不是调用 EndInvoke。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接