隐式与显式.NET线程

时间:2016-10-05 13:39:50

标签: c# multithreading sockets asyncsocket

因此,当我在任务管理器中查看我的套接字服务器解决方案时,我注意到应用程序正在创建并销毁线程(9到10到15回到10)。其中一些可以被MySQL Connector使用,但似乎登录到服务器将创建一个新线程。

我使用异步套接字接受连接,将其传递给PacketHandler实例,同时监听同时连接。我的问题是,如何判断代码将创建一个新线程?我从未明确写过需要创建一个线程,但它似乎是使用异步套接字的结果。我知道你在创建线程时应该保守一些(Cores * 2 =目标线程数),但当你不知道哪些代码会创建一个新线程时,这是一项艰巨的任务。

2 个答案:

答案 0 :(得分:3)

正确的异步代码只使用线程进行回调,通常只需要线程池线程,除非你做错了什么,否则不需要启动新的线程池线程。

MS.NET中当前的套接字实现肯定是这种情况。它们在异步事件上注册回调,并且线程池线程用于该回调的持续时间。除非您在回调中进行同步I / O操作,否则在线程池上创建更多线程毫无意义。

不要查看任务管理器 - 查看调试器。很可能你看到的线程与你正在使用的库无关。 .NET中有相当多的基础架构线程被创建和处理 - 特别是垃圾收集器。检查这些线程上的堆栈跟踪,您就会知道它们的作用。

修改: 在这种情况下,似乎MySQL Connector确实没有使用异步I / O.它只假装同步I / O使用多线程进行异步 - 换句话说,异步API是完全没用的bollocks :)并且因为它们使用线程池线程进行同步I / O,所以你可以使用它。我会遇到很多问题。要么使用不同的库,要么避免异步API - 它会骗你。

答案 1 :(得分:2)

不幸的是,MySQL Connector不提供真正的异步方法。它的Begin/End方法伪造了wrapping the synchronous version在线程中的异步执行:

public IAsyncResult BeginExecuteReader(CommandBehavior behavior)

{

  if (caller != null)

    Throw(new MySqlException(Resources.UnableToStartSecondAsyncOp));



  caller = new AsyncDelegate(AsyncExecuteWrapper);

  asyncResult = caller.BeginInvoke(1, behavior, null, null);

  return asyncResult;

}

其中AsyncExecuteWrapper是:

internal object AsyncExecuteWrapper(int type, CommandBehavior behavior)

{

  thrownException = null;

  try

  {

    if (type == 1)

      return ExecuteReader(behavior);

    return ExecuteNonQuery();

  }

  catch (Exception ex)

  {

    thrownException = ex;

  }

  return null;

}

结果,他们浪费了一个线程等待响应。三年前提交了一个错误but never got a real answer

这就是创建这个替代MySQLConnector项目的原因,它提供了真正的异步操作以及.NET Core支持,例如this method

    internal async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
    {
        using (var reader = (MySqlDataReader) await ExecuteReaderAsync(CommandBehavior.Default, ioBehavior, cancellationToken).ConfigureAwait(false))
        {
            do
            {
                while (await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
                {
                }
            } while (await reader.NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false));
            return reader.RecordsAffected;
        }
    }

您会看到ReadAsync也是一种正确的异步方法

差别很大,尤其是在Web应用程序中,因为它允许您使用较小的 VM实例来为相同的流量提供服务。或者相同的VM可以提供更多流量。在任何情况下,定价差异都是真实的。

这是因为异步网络操作实际上已卸载到驱动程序或主机。来自IO线程池的线程仅在网络驱动程序向应用程序发送响应时使用。在半虚拟化的情况下,卸载可以一直到主机。

另一方面阻止同步操作,甚至可能导致忙碌等待。这是因为同步原语用于... 将访问同步到共享资源。暂停线程开销,因此等待原语首先从一个Spinlock开始,并且只在一定时间过后才挂起该线程。

这就是为什么不考虑异步的Web应用程序在等待远程响应时最终会烧掉大量CPU