У меня есть два класса: LineWriter и AsyncLineWriter. Моя цель состоит в том, чтобы позволить вызывающему абоненту поставить в очередь несколько ожидающих асинхронных вызовов методов AsyncLineWriter, но под прикрытием никогда не позволять им работать параллельно; то есть они должны как-то стоять в очереди и ждать друг друга. Вот и все. Остальная часть этого вопроса дает полный пример, поэтому нет абсолютно никакой двусмысленности в том, что я действительно спрашиваю.
LineWriter имеет единственный синхронный метод (WriteLine), который запускается около 5 секунд:
public class LineWriter
{
public void WriteLine(string line)
{
Console.WriteLine("1:" + line);
Thread.Sleep(1000);
Console.WriteLine("2:" + line);
Thread.Sleep(1000);
Console.WriteLine("3:" + line);
Thread.Sleep(1000);
Console.WriteLine("4:" + line);
Thread.Sleep(1000);
Console.WriteLine("5:" + line);
Thread.Sleep(1000);
}
}
AsyncLineWriter просто инкапсулирует LineWriter и предоставляет асинхронный интерфейс (BeginWriteLine и EndWriteLine):
public class AsyncLineWriter
{
public AsyncLineWriter()
{
// Async Stuff
m_LineWriter = new LineWriter();
DoWriteLine = new WriteLineDelegate(m_LineWriter.WriteLine);
// Locking Stuff
m_Lock = new object();
}
#region Async Stuff
private LineWriter m_LineWriter;
private delegate void WriteLineDelegate(string line);
private WriteLineDelegate DoWriteLine;
public IAsyncResult BeginWriteLine(string line, AsyncCallback callback, object state)
{
EnterLock();
return DoWriteLine.BeginInvoke(line, callback, state);
}
public void EndWriteLine(IAsyncResult result)
{
DoWriteLine.EndInvoke(result);
ExitLock();
}
#endregion
#region Locking Stuff
private object m_Lock;
private void EnterLock()
{
Monitor.Enter(m_Lock);
Console.WriteLine("----EnterLock----");
}
private void ExitLock()
{
Console.WriteLine("----ExitLock----");
Monitor.Exit(m_Lock);
}
#endregion
}
Как я уже говорил в первом абзаце, моя цель - разрешить только одну незавершенную асинхронную операцию за раз. Мне бы действительно понравилось бы, если бы мне не нужно было использовать здесь замки; то есть, если BeginWriteLine может вернуть дескриптор IAsyncResult, который всегда возвращается немедленно и при этом сохраняет ожидаемое поведение, это было бы здорово, но я не могу понять, как это сделать. Таким образом, следующий лучший способ объяснить, что мне нужно, это просто использовать замки.
Тем не менее, мой раздел «Блокировка» работает не так, как ожидалось. Я ожидал бы, что приведенный выше код (который запускает EnterLock до начала асинхронной операции и ExitLock после завершения асинхронной операции) позволяет только одной ожидающей асинхронной операции выполняться в любой момент времени.
Если я запускаю следующий код:
static void Main(string[] args)
{
AsyncLineWriter writer = new AsyncLineWriter();
var aresult = writer.BeginWriteLine("atest", null, null);
var bresult = writer.BeginWriteLine("btest", null, null);
var cresult = writer.BeginWriteLine("ctest", null, null);
writer.EndWriteLine(aresult);
writer.EndWriteLine(bresult);
writer.EndWriteLine(cresult);
Console.WriteLine("----Done----");
Console.ReadLine();
}
I ожидаю, что увидит следующий вывод:
----EnterLock----
1:atest
2:atest
3:atest
4:atest
5:atest
----ExitLock----
----EnterLock----
1:btest
2:btest
3:btest
4:btest
5:btest
----ExitLock----
----EnterLock----
1:ctest
2:ctest
3:ctest
4:ctest
5:ctest
----ExitLock----
----Done----
Но вместо этого я вижу следующее, что означает, что они просто работают параллельно, как будто блокировки не действуют:
----EnterLock----
----EnterLock----
----EnterLock----
1:atest
1:btest
1:ctest
2:atest
2:btest
2:ctest
3:atest
3:btest
3:ctest
4:atest
4:btest
4:ctest
5:atest
5:btest
5:ctest
----ExitLock----
----ExitLock----
----ExitLock----
----Done----
Я предполагаю, что блокировки «игнорируются», потому что (и исправьте меня, если я ошибаюсь), я блокирую их все из одного потока. Мой вопрос: как может получить ожидаемое поведение? И «не используйте асинхронные операции» не является приемлемым ответом.
Для этого существует более крупный, более сложный, реальный сценарий использования, использующий асинхронные операции, где иногда невозможно иметь несколько действительно параллельных операций, но мне нужно по крайней мере эмулировать поведение, даже если это означает постановку в очередь операций и запускать их один за другим. В частности, интерфейс к AsyncLineWriter не должен изменяться, но его внутреннее поведение каким-то образом должно ставить в очередь любые асинхронные операции, которые он выполняет в поточно-ориентированном виде. Другой недостаток заключается в том, что я не могу добавить блокировки в WriteLine LineWriter's, потому что в моем реальном случае это метод, который я не могу изменить (хотя в этом примере это действительно дает ожидаемый результат).
Ссылка на некоторый код, предназначенный для решения аналогичной проблемы, может быть достаточно хороша, чтобы направить меня на правильный путь. Или, возможно, некоторые альтернативные идеи. Спасибо.
P.S. Если вам интересно, какой вариант использования может использовать такую вещь: это класс, который поддерживает сетевое соединение, в котором одновременно может быть активна только одна операция; Я использую асинхронные вызовы для каждой операции. Не существует очевидного способа, чтобы по-настоящему параллельные линии связи проходили через одно сетевое соединение, поэтому им нужно будет так или иначе ждать друг друга.