Ожидание завершения асинхронного метода в C # - PullRequest
0 голосов
/ 30 марта 2011

Пример кода:

    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 ]

4 голосов
/ 30 марта 2011

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);
    }
}

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

2 голосов
/ 30 марта 2011

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

0 голосов
/ 30 марта 2011

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

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

0 голосов
/ 30 марта 2011

Нить не закрывается. Монитор

Monitor.Exit(locker);

это проблема

0 голосов
/ 30 марта 2011

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

...