Are visual studio generated WebService clients thread safe?
We have created a service link in one of our projects.
We now instantiate this when the application starts. Also, when application statistics add an event handler for Service.Method1Completed
, Service.Method2Completed
ect.
Then on specific events we call Service.Method1Async
, Service.Method2Async
ect. note that these calls are issued by different threads.
But on some computers, the event handler never runs, so we started checking FirstChanceExceptions
how it turns out when this happens the following happens FirstChanceExceptions
.
System.Net.Sockets.SocketException Invalid argument was supplied.
vid System.Net.Sockets.Socket.SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, Int32 optionValue, Boolean silence)System.ObjectDisposedException Unable to access the remote object. Object name: System.Net.Sockets.NetworkStream. VID System.Net.Sockets.NetworkStream.UnsafeBeginWrite (Byte [] buffer, Int32 offset, Int32 size, AsyncCallback, object state)
System.Net.WebException The request was canceled.
vid System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult)
Is this the wrong way to use the Service Reference? And if so, how can I properly synchronize my calls when using async methods with events (note that the suck is on .net 4.0 and VS 2010, therefore await
missing.).
ServerCode:
<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
Server config:
<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>
Client code:
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
source to share
The client base cannot be thread safe *, so derived proxies are not, so you cannot use them that way.
You can create thread safe base class and custom generate proxy classes.
The parameter state
can be used to match the response with the outgoing request. You will find it in IAsyncResult.AsyncState in the callback event.
One thing to worry about is when multiple requests are sent through the same channel and the channel fails for whatever reason.
* Look at this question as well: Is WCF ClientBase thread safe?
source to share