Ожидание завершения асинхронного метода в C#

Образец кода:

    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 ответов

Решение

Monitor.Enter(locker) находится в текущем потоке, Monitor.Exit находится в другом потоке, так как он вызывается из вашего текущего потока.
Таким образом, вам также нужно использовать Monitor.Wait и Monitor.Pulse, но ManualResetEvents проще в вашем случае.

Monitor.Enter а также Monitor.Exit звонки должны быть сделаны в той же теме. В вашем образце вы звоните 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);
    }
}

Тем не менее, в вашем сценарии вы можете просто вызвать делегата синхронно.

Нить не должна закрывать монитор

Monitor.Exit(locker);

это проблема

Я думаю, что вы можете ждать завершения события, используя класс ManualResetEvent. Извините, у меня нет опыта работы с монитором. Но я использую классы ManualResetEvent / AutoResetEvent для тестирования обратных вызовов.

Эта ошибка очень вводит в заблуждение. Это на самом деле не означает, как это выглядит. Это на самом деле означает, что Monitor.Exit называется до того, как вы позвонили Monitor.Enter на объекте синхронизации.

Ваш Monitor.Exit вызов происходит в другом потоке от Monitor.Enter вызов - оба не видят объекты синхронизации друг друга.

Другие вопросы по тегам