等待异步方法以C#结尾

时间:2011-03-30 08:04:29

标签: c# multithreading thread-safety monitor

示例代码:

    class Program
{
    static readonly object locker = new object();

    static void Main(string[] args)
    {
        Func();
        Func();

        Thread.Sleep(6000);
    }

    static void Func()
    {
        Monitor.Enter(locker);
        Action act = () =>
                    {
                        Thread.Sleep(2000);
                    };
        act.BeginInvoke(a =>
                        {
                            Console.WriteLine("exiting..");
                            Monitor.Exit(locker);
                        }, null);
        Console.WriteLine("Func done...");
    }
}

理想情况下,控制台会打印出来:

Func done...
exiting...
Func done...
exitting...

但是,我得到了:

Func done...
Func done...
exitting...

然后Monitor.Exit抛出异常

  

从非同步代码块调用对象同步方法。

这里有什么错误?实现这一目标的首选方法是什么?

5 个答案:

答案 0 :(得分:4)

Monitor.EnterMonitor.Exit调用必须在同一个线程上进行。在您的示例中,您在UI线程上调用Monitor.Enter,在为Monitor.Exit触发的异步调用创建的线程上调用BeginInvoke

如果你想等待Func内的异步操作完成,你可以这样做:

class Program
{
    static void Main(string[] args)
    {
        Func();
        Func();

        Thread.Sleep(6000);
    }

    static void Func()
    {
        Action act = () =>
                {
                    Thread.Sleep(2000);
                };
        IAsyncResult actAsyncResult = act.BeginInvoke(a =>
                {
                    Console.WriteLine("exiting..");
                }, null);

        Console.WriteLine("Func done...");

        act.EndInvoke(actAsyncResult);
    }
}

然而,在您的场景中,您可以只是同步调用委托。

答案 1 :(得分:2)

Monitor.Enter(locker)在当前线程上,Monitor.Exit位于不同的线程上,因为它是从当前线程调用的。
因此,您还需要使用Monitor.Wait和Monitor.Pulse,但在您的情况下,ManualResetEvents更容易。

答案 2 :(得分:0)

我认为您可以使用ManualResetEvent class等待活动完成。对不起,我对Monitor没有任何经验。但我使用ManualResetEvent / AutoResetEvent类来测试回调。

答案 3 :(得分:0)

线程不关闭监视器

Monitor.Exit(locker);

这是一个问题

答案 4 :(得分:0)

此错误非常具有误导性。它实际上并不意味着它的意思。它实际上意味着在您在同步对象上调用Monitor.Exit之前调用Monitor.Enter

您的Monitor.Exit通话发生在与Monitor.Enter通话不同的线程上 - 两人没有看到对方的同步对象。