4

我正在开发一个类库,它将为 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 正是我使用它的方式,只有“静态 X 作为新对象”在必须锁定的函数中创建同步对象。

是不是因为回调实际上来自 .Net 框架之外的 win32 线程,所以 SyncLock 在这里不起作用?

4

2 回答 2

8
    Static OneAtATime As New Object

Static 关键字是 VB.NET 实现者脖子上的一块相当沉重的磨石。他们不得不支持它,因为它在以前的 Visual Basic 版本中经常使用,省略它会给想要更新他们的工具的程序员带来太多的困难。

但是它的遗留行为与线程非常不兼容,线程是 .NET 中非常强烈支持的功能。以前不是问题,因为旧的 VB 版本不支持创建线程。为该语句生成的 MSIL 代码量是巨大的。您应该使用 ildasm.exe 实用程序进行查看。

它是巨大的,因为它需要做什么。这是第一次输入方法时只初始化一次变量。不是很困难,它会自动生成另一个布尔变量来跟踪。但更困难的部分是为每个单独的线程执行一次。换句话说,它具有 [ThreadStatic] 行为。

这就是杀死你的原因,每个线程都有自己的 SyncLock。这就是为什么您根本没有观察到同步的原因:) 您需要将其移出方法并声明它Shared

于 2015-03-26T15:09:45.340 回答
2

我以前从未见过staticVB中局部变量的用法。存在这样的东西对我来说是新闻。我建议您改用传统方式并使用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
于 2015-03-26T14:32:47.587 回答