【问题标题】:Are visual studio generated WebService clients threadsafe?Visual Studio 生成的 Web 服务客户端线程安全吗?
【发布时间】:2014-08-26 15:02:32
【问题描述】:

我们在其中一个项目中创建了服务引用。

现在我们在应用程序启动时创建一个 this 的实例。 此外,当应用程序统计时,我们为Service.Method1CompletedService.Method2Completed 等添加事件处理程序。

然后在特定事件上,我们调用Service.Method1AsyncService.Method2Async 等。请注意,这些调用是由不同的线程发出的。

但在某些计算机上,事件处理程序从未被触发,因此我们开始检查 FirstChanceExceptions,因为当这种情况发生时,会发生以下 FirstChanceExceptions

System.Net.Sockets.SocketException 提供的参数无效。
vid System.Net.Sockets.Socket.SetSocketOption(SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionValue, Boolean 沉默)

System.ObjectDisposedException 无法访问已处置的对象。 对象名:System.Net.Sockets.NetworkStream。视频 System.Net.Sockets.NetworkStream.UnsafeBeginWrite(字节 [] 缓冲区,Int32 偏移量、Int32 大小、AsyncCallback 回调、对象状态)

System.Net.WebException 请求被取消。
vid System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)

这是使用服务参考的无效方式吗? 如果是我如何在使用带有事件的异步方法时正确使用同步我的调用(注意我在 .net 4.0 和 VS 2010 上很烂,所以await 不在图片中......)。

服务器代码:

<ServiceContract()>
Public Interface IService1

    <OperationContract()>
    Function GetData(ByVal value As Integer) As String

End Interface

<ServiceBehavior(ConcurrencyMode:=ConcurrencyMode.Multiple, InstanceContextMode:=InstanceContextMode.Single, UseSynchronizationContext:=False)>
Public Class Service1
    Implements IService1

    Public Sub New()
    End Sub

    Public Function GetData(ByVal value As Integer) As String Implements IService1.GetData
        Return String.Format("You entered: {0}", value)
    End Function

End Class

服务器配置:

<system.serviceModel>
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
        <serviceMetadata httpGetEnabled="true"/>
        <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
        <serviceDebug includeExceptionDetailInFaults="false"/>
        <serviceThrottling maxConcurrentCalls="1000" maxConcurrentSessions="1000" maxConcurrentInstances="1000" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <bindings>
    <customBinding>
      <binding>
        <binaryMessageEncoding>
          <readerQuotas maxArrayLength="5242880" />
        </binaryMessageEncoding>
        <httpTransport maxBufferPoolSize="52428800" maxReceivedMessageSize="5242880" maxBufferSize="5242880" authenticationScheme="Anonymous" />
      </binding>
    </customBinding>
  </bindings>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>

客户端代码:

Imports System.Threading
Imports System.IO

Module Module1
    Dim errorQueue As New System.Collections.Concurrent.ConcurrentBag(Of String)
    Dim count As Integer

    Sub Main()
        AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf CurrentDomain_UnhandledException
        AddHandler AppDomain.CurrentDomain.FirstChanceException, AddressOf AppDomain_FirstChanceException

        MultipleClientInstances()
        Console.WriteLine("Calls are in progress, press any key when they are done!")
        Console.ReadKey()

        Thread.MemoryBarrier()
        errorQueue.Add("Number of requests remaining " + count.ToString())
        Dim line As String = ""
        Using writer As New StreamWriter("output.log")
            While (errorQueue.TryTake(line))
                writer.WriteLine(line)
            End While
        End Using
    End Sub

    Private Function GetClient() As ServiceReference1.Service1Client
        Dim client As New ServiceReference1.Service1Client()
        AddHandler client.GetDataCompleted, AddressOf client_GetDataCompleted
        client.Open()
        Return client
    End Function

    Private Sub MultipleClientInstances()
        Console.WriteLine("Making calls!")
        For i As Integer = 0 To 10
            Dim t As New Thread(AddressOf MakeCallsWithNewClients)
            t.Start()
        Next
    End Sub

    Private Sub MakeCallsWithNewClients()
        For i As Integer = 0 To 400
            Interlocked.Increment(count)
            Dim client As ServiceReference1.Service1Client = GetClient()
            client.GetDataAsync(i, True)

            While (Thread.VolatileRead(count) > 20)
                Thread.Sleep(5)
            End While
        Next
    End Sub

    Private Sub client_GetDataCompleted(sender As Object, e As ServiceReference1.GetDataCompletedEventArgs)
        Dim value As Integer = Interlocked.Decrement(count)
        Console.WriteLine(value)

        Dim client As ServiceReference1.Service1Client = CType(sender, ServiceReference1.Service1Client)
        RemoveHandler client.GetDataCompleted, AddressOf client_GetDataCompleted
        client.Close()
    End Sub

    Private Sub CurrentDomain_UnhandledException(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
        If (e.ExceptionObject IsNot Nothing AndAlso e.ExceptionObject.GetType().IsSubclassOf(GetType(Exception))) Then
            If (e.IsTerminating) Then
                Console.WriteLine("Fatal exception occurred termination application, " + CType(e.ExceptionObject, Exception).ToString())
            Else
                Console.WriteLine("Unhandled exception occurred, " + CType(e.ExceptionObject, Exception).ToString())
            End If
        Else
            If (e.IsTerminating) Then
                Console.WriteLine("Fatal exception occurred termination application, " & e.ExceptionObject.ToString())
            Else
                Console.WriteLine("Unhandled exception occurred, " & e.ExceptionObject.ToString())
            End If
        End If
        errorQueue.Add("UnhandledException: " + e.ExceptionObject.ToString())
    End Sub

    Private Sub AppDomain_FirstChanceException(ByVal sender As Object, ByVal e As Runtime.ExceptionServices.FirstChanceExceptionEventArgs)
        Console.WriteLine("FirstChanceException: " + e.Exception.ToString())
        errorQueue.Add("FirstChanceException: " + e.Exception.ToString())
    End Sub
End Module

【问题讨论】:

    标签: .net vb.net multithreading web-services wcf


    【解决方案1】:

    它们不是线程安全的(例如ClientBase 不是)。但是它们的创建和销毁成本很低。每个线程创建一个,甚至每个调用创建一个。无需同步。

    【讨论】:

    • 我创建了一个新项目,我在其中对 Web 服务进行了大规模调用,每个请求都有一个客户端,我很遗憾地说我仍然收到 ObjectDisposedException(它们只是第一次机会例外,我从来没有得到回调完成...)
    • 好的,该代码看起来不错。线程是一个风险领域,但除了计数变量之外,我没有看到任何共享状态。使您的代码单线程用于测试目的。这个特定问题是否仍然存在?
    • 我只是尝试将代码更改为仅在 MultipleClientInstances 中启动一个线程,然后我将调用次数从 400 增加到 4000,但仍然出现错误....
    • 那么这不是线程问题。通过 HTTP 调用服务并使用 Fiddler 查看 HTTP 和网络级错误。你发现了什么错误?如果有,请发布有意义的截图。
    • 当我针对 IIS 运行客户端时,此错误停止发生,但我仍然感到不安的是,服务器上的某些错误可能导致客户端内存泄漏...
    【解决方案2】:

    ClientBase 可能不是线程安全的*,因此派生代理也不是,因此您不能以这种方式使用它们。

    您可以创建一个线程安全的基类并自定义生成代理类。

    state 参数可用于将响应与传出请求匹配。您会在回调事件的 IAsyncResult.AsyncState 中找到它。

    要担心的一件事是,当多个请求通过同一个通道发送并且通道由于某些/任何原因而出现故障时。

    *也看看这个问题:Is WCF ClientBase thread safe?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-20
      • 2011-08-14
      • 2011-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-07
      相关资源
      最近更新 更多