Ожидание завершения асинхронного метода в 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
вызов - оба не видят объекты синхронизации друг друга.