Почему SyncLock здесь не работает?

Я работаю над библиотекой классов, которая обеспечит асинхронную связь с приложениями CLR.

В SslStream есть асинхронные операции чтения (BeginRead) с одной подпрограммой обратного вызова, совместно используемой несколькими потоками. Я не хочу, чтобы обратные вызовы обрабатывались параллельно во время отладки, поэтому я создал критическую секцию:

Private Sub Callback_Read(ByVal ar As IAsyncResult)
   Static OneAtATime As New Object
   SyncLock OneAtATime
      Dim ThisSslStream As SslStream = DirectCast(ar.AsyncState, SslStream)
      ...
   End SyncLock
End Sub

К моему удивлению, это не работает, по крайней мере, когда я устанавливаю точку останова в блоке SyncLock. Обратные вызовы для нескольких потоков выполняются в нем одновременно, без ожидания в точке входа, пока предыдущий поток не покинул ее.

Один шаг - это кошмар, особенно когда потоки закрываются (закрываются) одновременно: выполнить строку для потока 1, выполнить строку для потока 2, выполнить следующую строку для 1, выполнить следующую строку для 2 и т. Д. На протяжении всего блок.

Я подумал, может быть, вам нужно нечто большее, чем просто "новый объект", но потом я увидел, что здесь есть по крайней мере один ответ здесь о переполнении стека, который иллюстрирует SyncLock именно так, как я его использую, просто "Static X as New Object" создать объект синхронизации внутри функции, которая должна быть заблокирована.

Это потому, что обратный вызов на самом деле приходит из потока win32 за пределами.Net Framework, что SyncLock здесь не работает?

2 ответа

Решение

Я никогда не видел использование static локальные переменные в VB раньше. То, что такая вещь существовала, было для меня новостью. Я бы посоветовал вам сделать это обычным способом и использовать shared переменная класса.

public Class Test
   Private shared SyncRoot As Object = new Object()

   Private Sub Callback_Read(ByVal ar As IAsyncResult)
      SyncLock SyncRoot 
         Dim ThisSslStream As SslStream = DirectCast(ar.AsyncState, SslStream)
         ...
      End SyncRoot
   End Sub

End Class
    Static OneAtATime As New Object

Ключевое слово Static было довольно тяжелым жерновом на шее разработчиков VB.NET. Они должны были поддерживать его, потому что он часто использовался в предыдущих выпусках Visual Basic, если его пропустить, это создаст слишком большие трудности для программистов, которые хотят обновить свои инструменты.

Но его устаревшее поведение было очень несовместимо с многопоточностью, функцией, которая очень сильно поддерживается в.NET. Не проблема раньше, потому что старые версии VB не поддерживали создание потоков. Количество кода MSIL, сгенерированного для этого оператора, огромно. Вы должны посмотреть с помощью утилиты ildasm.exe.

Это массивно из-за того, что ему нужно сделать. Который инициализирует переменную только один раз, при первом входе в метод. Не очень сложно, он автоматически генерирует другую логическую переменную, которая отслеживает. Но более сложная часть - сделать это один раз для каждого отдельного потока. Другими словами, он имеет поведение [ThreadStatic].

Это то, что вас здесь убивает, каждый поток имеет свой собственный SyncLock. Вот почему вы вообще не наблюдали синхронизацию:) Вам нужно убрать ее из метода и объявить ее Shared,

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