Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Would a ThreadPool or a Task be the correct thing to use for a server?

I'm trying to convert some code from just creating a new thread to run a function to making it use a Thread Pool or even the Task Paralleling Library. I'm doing this since I know that despite the Worker Thread's Function may run indefinitely (in theory), each thread will spend most of it's time doing nothing. I also want something to minimize the overhead for the creation and destruction of the Worker Threads, as connections may timeout or new ones get created. That - and seeing CLRProfiler show 7836 threads were finalized in/after a 62 hour test run is a little unnerving, with a single (if finicky) device sending a message.

Here's what I want to do:

Main Thread.

1.) Have a TCPListener accept a TcpClient

2.) Fire off a Worker Thread which uses that TcpClient

3.) Go back to step 1 if we haven't been told to stop.

Worker Thread (To used in the Pool/Tasks)

1.) Check to see if we have a message from the TcpClient

2.) If so, parse message, send off to database, and sleep for 1 second.

3.) Otherwise, sleep for 1 millisecond.

4.) Go back to step 1 if we haven't been told to stop and have not timed out.

Here's the original approach:

private AutoResetEvent connectionWaitHandle = new AutoResetEvent(false);
private static bool stop = false;

private void MainThread()
{
    TcpListener universalListener = new TcpListener(IPAddress.Any, currentSettings.ListeningPort);
    universalListener.Start();

    while (!stop)
    {
        IAsyncResult result = universalListener.BeginAcceptTcpClient(WorkerThread, universalListener);
        connectionWaitHandle.WaitOne();
        connectionWaitHandle.Reset();
    }
}

private void WorkerThread(IAsyncResult result)
{
    TcpListener listener = result.AsyncState as TcpListener;

    if (listener == null)
    {
        connectionWaitHandle.Set();
        return;
    }

    TcpClient client = listener.EndAcceptTcpClient(result);
    connectionWaitHandle.Set();

    NetworkStream netStream = null;

    bool timedout = false;

    try
    {
        while (!timedout && !stop)
        {
            if (client.Available > 0)
            {
                netStream = client.GetStream();

                //Get and Parse data here, no need to show this code
                //The absolute fastest a message can come in is 2 seconds, so we'll sleep for one second so we aren't checking when we don't have to.
                Thread.Sleep(1000);
            }
            else
            {
                //Sleep for a millisecond so we don't completely hog the computer's resources.
                Thread.Sleep(1);
            }

            if (/*has timed out*/)
            {
                timedout = true;
            }
        }
    }
    catch (Exception exception)
    {
        //Log Exception
    }
    finally
    {
        client.Close();
    }
}

I've tried replacing the universalListener.BeginAcceptTcpClient(...) et. all with

(new Task.TaskFactory.FromAsync<TCPClient>(universalListener.BeginAcceptTcpClient, universalListener.EndAcceptTcpClient, universalListener).ContinueWith(WorkerThread);

as well as removing the AutoResetEvent connectionWaitHandle code, but the Worker Thread seemed to only fire once.

I'm also a little unsure if I should even try to use a Thread Pool or a Task, as everything I could find about Thread Pools and Tasks (official documentation or otherwise) seems to indicate they should be used with threads that have an extremely short lifespan.

My questions are:

  1. Is the Thread Pool or even Tasks from the Task Parallel Library appropriate for Long-lived, but mostly wheel spinning, Threads?
  2. If so, how would I best implement the correct pattern?
  3. If so, did I have the right idea on using TaskFactory.FromAsync(...).ContinueWith(...)?
like image 427
Tory Avatar asked Nov 12 '12 20:11

Tory


People also ask

What is the use of ThreadPool?

A thread pool helps mitigate the issue of performance by reducing the number of threads needed and managing their lifecycle. Essentially, threads are kept in the thread pool until they're needed, after which they execute the task and return the pool to be reused later.

What is a ThreadPool is it better than using several simple threads?

A thread pool is a collection of threads which are assigned to perform uniformed tasks. The advantages of using thread pool pattern is that you can define how many threads is allowed to execute simultaneously.

What is a thread pool and why are they beneficial?

In computer programming, a thread pool is a software design pattern for achieving concurrency of execution in a computer program. Often also called a replicated workers or worker-crew model, a thread pool maintains multiple threads waiting for tasks to be allocated for concurrent execution by the supervising program.


1 Answers

By default, the TPL will use the Thread Pool. So, either way you are using the Thread Pool. The question is just which programming model you use to access the pool. I strongly suggest TPL, as it provides a superior programming abstraction.

The threads in your example are actually not spinning (burning CPU cycles), but rather blocking on a wait handle. That is quite efficient and does not consume a thread while blocked.

UPDATE

The TaskFactory.FromAsync(...).ContinueWith(...) pattern is appropriate. For a great list of reasons, see this question.

If you are using C# 5 / .NET 4.5, you can use async/await to express your code pattern even more compactly.

http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2010/11/22/c-5-0-rise-of-the-task.aspx

like image 104
Eric J. Avatar answered Sep 25 '22 11:09

Eric J.