none
use .Net UdpClient in a multithreaded env RRS feed

  • Question

  • I have an instance of a class (lets call it A) which serves some threads, this instance only sends UDP packets via the UdpClient class. It initialize the the UdpClient in its constructor and only serves to send the packets.

    It looks something like:

    public class A{
    
    private UdpClient m_Client;
    public class A(string host, int port){
    
        m_Client = new UdpClient(host, port);
    }
    
    public void Send(string dataToSend){
    
     var data= Encoding.ASCII.GetBytes(dataToSend);
     client.BeginSend(data, data.Length, null, null);
    }
    
    }

    My questions is:

    I know that UdpClient isn't thread-safe (according to MSDN documentation), what is the best way to support multithreaded without using locking mechanism?

    1) On each send create new instance of the UdpClient? (just use some local UdpClient var). performance?

    2) Use ThreadLocal for the UdpClient? but what about the disposing of the UdpClient in this situation?

    3) Any other solution?

    Thanks,

    Tomer



    • Edited by TomerPel Thursday, February 14, 2013 12:39 PM
    • Moved by Lisa Zhu Friday, February 15, 2013 9:16 AM
    Thursday, February 14, 2013 7:52 AM

Answers

  • Thread safety is expensive.  Therefore it doesn't make sense to use it when it is isn't needed.  You're going to run into problems if you try to use the same client on multiple threads.  Hence it is not recommended and the class is not thread safe.

    It is just a matter of time before you start seeing bad data go across the wire.  It will likely occur once in a while and you're going to spend a lot of time debugging it.  Threading and multiple clients is a very common approach and I doubt you're going to see any sort of problems with the performance.  But if your current solution seems to be working for you then continue to use it.  When problems occur then switch over to the more reliable approach of a client pool.

    • Marked as answer by TomerPel Sunday, February 17, 2013 9:12 AM
    Saturday, February 16, 2013 7:40 PM
    Moderator

All replies

  • Anyone knows what is the performance of the initialization of the UdpClient? should I the 1) method?
    Thursday, February 14, 2013 12:40 PM
  • Initializing UdpClient isn't that expensive but if you're always talking to the same host then you're going to want to use the same client.  If you don't then it is possible that multiple clients will be trying to send data to the same host and the host is going to end up getting garbage.  It is pretty much required that all communication to the same host/port be serialized otherwise things will just not work on the remote end.  As such a single client is sufficient.  Furthermore locking is going to be needed to ensure only a single thread uses the client at a time.  Given your class A (which I would probably rename but otherwise stick with) you'll need to wrap the send request in a lock.  But since you're using BeginSend you'll actually need to use some sort of flag in your class to control whether it can send or not. 

    You'll then need to either expose an instance of A as a static so there is only once instance or (more likely as a singleton).  Alternatively you could create yet another wrapper class that contains a static instance of A but is otherwise an instance class.  The difference between the two approaches boils down to how you'll be writing your per-thread code.  In the former case you'll be referencing an existing instance in each thread.  In the latter case you'll be creating a new, temp instance of a class that itself wraps the static client instance.  Choose the approach based upon how you want your threaded code to behave.

    Note that if you're already using threading to make calls to Send then there is no benefit in using BeginSend because your thread is going to block waiting for another thread.  Standard cases will have a single thread per remote host.  You can review this after you get your single instance stuff working.

    Michael Taylor - 2/14/2013
    http://msmvps.com/blogs/p3net

    Thursday, February 14, 2013 3:15 PM
    Moderator
  • Hi Tomer,

    From your description, I ‘d like to move this post to  the most related forum.

    There are more  experts in this aspect, so you will get  better support and  may have more luck getting answers.

    Thanks for your understanding.

    Regards,


    Lisa Zhu [MSFT]
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, February 15, 2013 9:16 AM
  • Thanks Lisa!
    • Edited by TomerPel Friday, February 15, 2013 1:58 PM
    Friday, February 15, 2013 1:46 PM
  • Michael thanks for your response.

    However, I don't want to use the lock mechanism, from performance concerns.

    I did some test which used the create and dispose pattern for each Send and I experienced with performance degradation...

    I looked over the UdpClient (.Net 4.5) code (via reflector) and it seems that using my current implementation will probably work with no problems. However, since Microsoft desclaimer regarding this class is that it isn't thread-safe, I don't think that I can count on the code that I saw.

    Can anyone tell me what are the best practices for using the UdpClient? Notice that I need to send the Udp packets to the same host each time and I only need to use the BeginSend method.



    Friday, February 15, 2013 1:58 PM
  • The overhead of a lock is less than what it is sending data over the network.  I doubt that is your problem.  I suspect your problem is that you don't like the overhead of blocking a thread.  But if you're going to use the same client you have no choice.  Here's what's going to happen if you try to use the same client from multiple threads at the same time:

    Thread A tries to send a packet of say 1000 bytes. 
    BeginSend is called and 200 bytes are sent (the packets are fragmented based upon the network stack)

    Thread B tries to send a packet of say 5000 bytes.
    BeginSend is called and 400 bytes are sent.

    Thread A's BeginSend sends the next batch of bytes (400).
    Thread B's BeginSend sends the next batch of bytes (500).

    ...

    On the server it gets a mix of thread A's and thread B's packets and it has no way of telling the difference.  The larger the packets the more likely it is to occur.  If you're only ever sending small packets (say 256 or less) then you are unlikely to see this issue.

    If you need to send data across multiple threads at the same time then you have to use multiple clients (provided the server supports multiple clients from the same IP address). To avoid the overhead of creating clients you can use a client pool (like ADO.NET does). The thread asks the client pool for a client. The client pool grabs a client that has already been created from the available queue. The client is moved to the busy queue. When the thread is done with the client it returns it to client pool so it gets moved from busy back to available. If a client is requested and there are none available then a new client is created and returned.  Assuming your thread counts don't bounce around a lot then in general you'll see consistently small overhead after the initial startup. But note that you should put a cap on the # of clients you create because a sudden request for 1000s of clients could waste a lot of resources. In ADO.NET the default cap is 100. Any clients allocated beyond that are simple cleaned up when they are returned to the pool. The upper bound for your case would need to be set based upon usage patterns.

    Michael Taylor - 2/15/2013
    http://msmvps.com/blogs/p3net

    Friday, February 15, 2013 2:52 PM
    Moderator
  • My problem in regarding to the lock mechainsm is indeed the blocking of a thread.

    I believe that since the UdpClient eventually use the Socket class (which is thread-safe according to the MSDN documentation), and since I'm using only the BeginSend method, then the current implementation should work with no problems - as long as Microsoft won't change the UdpClient implementation in th next .Net versions.

    In the situation which you've described, where the packets are getting mixed up because of different threads, well this situation shouldn't happen even for big packets (more than 256 bytes), due to the fact that the BeginSend of the UdpClient is eventually the BeginSend of the Socket class (which is thread-safe) , again this is correct only for my current implementation - where I only initialize the UdpClient once and only use the BeginSend method. Notice that eventually the implementation of the Socket class might create thread for each connection handling (it has the ExternalThreading attribute), so actually I'm throwing the threading hadnling on the Socket responsiblity rather than using some Client pool as you suggested.

    Notice that I also created some test which creates several threads which use the same UdpClient and send packets larger than 256 bytes and the server got the messages with no mix-up - just wanted to make sure that it works as what I thought...

    So my current implementation actually works but as I said I don't want to be dependent on changes in the UdpClient in the next .Net releases. 

    I believe that the Client pool which you've suggested would obviously handle the threading issue safely, but in the current implentation it is already handled and more efficient eventually. Correct me if I'm worng?

    It is interesting, however to know how other people handled these situations, is it good enough to create and dispose the UpdClient for each send request? Do we really need to use some client pool for solving this? Why is the UdpClient isn't thread-safe from Microsoft perspective? can it be thread-safe with several minor changes like the Socket class is?

    Thanks,

    Tomer




    • Edited by TomerPel Saturday, February 16, 2013 10:59 AM
    Saturday, February 16, 2013 10:43 AM
  • Thread safety is expensive.  Therefore it doesn't make sense to use it when it is isn't needed.  You're going to run into problems if you try to use the same client on multiple threads.  Hence it is not recommended and the class is not thread safe.

    It is just a matter of time before you start seeing bad data go across the wire.  It will likely occur once in a while and you're going to spend a lot of time debugging it.  Threading and multiple clients is a very common approach and I doubt you're going to see any sort of problems with the performance.  But if your current solution seems to be working for you then continue to use it.  When problems occur then switch over to the more reliable approach of a client pool.

    • Marked as answer by TomerPel Sunday, February 17, 2013 9:12 AM
    Saturday, February 16, 2013 7:40 PM
    Moderator
  • Ok, Thanks Michael!
    Sunday, February 17, 2013 9:12 AM
  • I do not like locking either. It degradates performances, it pollutes code, I usually prefer writing some extra code to serialize data.

    In you case, I would do the following

    public class AsyncUdpSender
    {
    	private BlockingCollection<Action<UdpClient>> m_commandQueue;
    
    	public static AsyncUdpSender CreateAndStart(string host, int port, CancellationToken token)
    	{
    		var queue = new BlockingCollection<Action<UdpClient>>();
    		var dequeueThread = new Thread(() => {
    				using(var client = new UdpClient(host, port))
    				{
    					while(token.IsCancellationRequested == false)
    					{
    						var command = queue.Take(token);
    						command(client);
    					}
    				}
    			});
    		dequeueThread.Start();
    		return new AsyncUdpSender(queue);
    
    	}
    
    	private AsyncUdpSender(BlockingCollection<Action<UdpClient>> commandQueue)
    	{
    		m_commandQueue = commandQueue;
    	}
    
    	public void Send(string data)
    	{
    		m_commandQueue.Add(client => client.Send(data));
    	}
    }

    There is some boilerplate, I agree. But everything is explicit.

    Explained: when you call the Send(), it enqueues the command and returns (the calling thread is not blocking). The reading thread is executing commands on a real UDPClient which, by the way, has also been created on the same thread. 

    It's importa to stop the reading thread, the above code is using a cancellation token but it's missing some try...catch to trap timeout and other possible issues. But I hope the above code gives you some hints.

    A

    Saturday, September 9, 2017 12:04 PM