6

我正在尝试将一些代码从创建一个新线程以运行一个函数转换为使其使用线程池甚至任务并行库。我这样做是因为我知道尽管工作线程的函数可能会无限期地运行(理论上),但每个线程将花费大部分时间什么都不做。我还想要一些东西来最小化创建和销毁工作线程的开销,因为连接可能会超时或创建新的连接。那 - 并且看到 CLRProfiler 显示 7836 个线程在 62 小时测试运行中/之后完成,这有点令人不安,单个(如果挑剔)设备发送消息。

这是我想要做的:

主线程。

1.) 让 TCPListener 接受 TcpClient

2.) 触发一个使用该 TcpClient 的工作线程

3.) 如果我们没有被告知停止,请返回第 1 步。

工作线程(用于池/任务)

1.) 检查我们是否有来自 TcpClient 的消息

2.) 如果是,则解析消息,发送到数据库,然后休眠 1 秒。

3.) 否则,休眠 1 毫秒。

4.) 如果我们没有被告知停止并且没有超时,请返回步骤 1。

这是原始方法:

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();
    }
}

我试过替换 UniversalListener.BeginAcceptTcpClient(...) 等。所有与

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

以及删除 AutoResetEvent connectionWaitHandle 代码,但工作线程似乎只触发一次。

我也有点不确定我是否应该尝试使用线程池或任务,因为我能找到的关于线程池和任务(官方文档或其他)的所有内容似乎都表明它们应该与具有极寿命短。

我的问题是:

  1. 线程池甚至任务并行库中的任务是否适合长期存在但主要是轮子旋转的线程?
  2. 如果是这样,我将如何最好地实施正确的模式?
  3. 如果是这样,我对使用 TaskFactory.FromAsync(...).ContinueWith(...) 有正确的想法吗?
4

1 回答 1

6

默认情况下,TPL 将使用线程池。因此,无论哪种方式,您都在使用线程池。问题是您使用哪种编程模型来访问池。我强烈建议使用 TPL,因为它提供了卓越的编程抽象。

您示例中的线程实际上并没有旋转(消耗 CPU 周期),而是阻塞了等待句柄。这是非常有效的,并且在阻塞时不会消耗线程。

更新

TaskFactory.FromAsync(...).ContinueWith(...) 模式是合适的。有关原因的详细列表,请参阅此问题

如果您使用的是 C# 5 / .NET 4.5,则可以使用 async/await 更紧凑地表达您的代码模式。

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

于 2012-11-12T20:37:41.587 回答