【问题标题】:Thread.Abort doesn't seem to throw a ThreadAbortException because of AcceptSocket由于 AcceptSocket,Thread.Abort 似乎没有抛出 ThreadAbortException
【发布时间】:2011-03-09 09:55:05
【问题描述】:

我在以下线程上调用ChannelServer.ListeningThread.Abort,但似乎没有发生任何事情。我想更具体一些,但我想不出更多。似乎没有抛出ThreadAbortException,并且无论阻塞监听器如何都应该抛出这个异常(它在阻塞接收的线程上完美运行)。

重要编辑:使用 ManualResetEvent.WaitOne 而不是 AcceptSocket,正如 Lyrik 建议的测试一样,它可以完美运行。 AcceptSocket怎么会屏蔽ThreadAbortException

LINK:这个论坛帖子似乎讨论了同样的问题,虽然我想不通:http://www.tek-tips.com/viewthread.cfm?qid=319436&page=413

ChannelServer.ListeningThread = new Thread(new ThreadStart(delegate()
{
    Log.Inform("Waiting for clients on thread {0}.", Thread.CurrentThread.ManagedThreadId);

    while (true)
    {
        try
        {
            new Thread(new ParameterizedThreadStart(ChannelClientHandler.Initialize)).Start(ChannelServer.Listener.AcceptSocket());
        }
        catch (ThreadAbortException)
        {
            Log.Inform("Aborted client listening thread {0}.", Thread.CurrentThread.ManagedThreadId);
            break;
        }
    }
}));
ChannelServer.ListeningThread.Start();

【问题讨论】:

  • 我无法想象这是做任何你想做的事情的正确方法。你真的不应该产生这么多线程。
  • @Adam,这是服务器上的一段普通代码...为每个接受的连接启动一个新线程。
  • 哎呀,这在任何东西上都是不正常的,除了一个微不足道的服务器。每个连接的线程通常会在扩展时引起问题。
  • 这是一个微不足道的服务器,这不是问题的重点。 :)
  • 顺便说一句,因为这里的行为似乎是调用阻塞了内核(并且托管线程上的 Abort 不足以让它解除阻塞),根据 MSDN 页面,您可能想要在 Pending 上循环:“如果您想避免阻塞,请使用 Pending 方法来确定传入连接队列中是否有可用的连接请求。”

标签: c# .net multithreading thread-abort threadabortexception


【解决方案1】:

这里是一个简单的AcceptSocket2 扩展方法(对不起,对于名称缺乏想象力......)。 它的工作原理与原来的 AcceptSocket 方法完全相同。

using System;
using System.Net.Sockets;
using System.Threading;

/// <summary>
/// Extensions to TcpListener
/// </summary>
public static class TcpListenerExtensions
{
    /// <summary>
    /// Accepts a pending connection request.
    /// </summary>
    /// <param name="tcpListener">The TCP listener.</param>
    /// <returns>
    /// A <see cref="T:System.Net.Sockets.Socket" /> used to send and receive data.
    /// </returns>
    /// <exception cref="T:System.InvalidOperationException">The listener has not been started with a call to <see cref="M:System.Net.Sockets.TcpListener.Start" />.</exception>
    /// <PermissionSet><IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /><IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence" /><IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true" /></PermissionSet>
    public static Socket AcceptSocket2(this TcpListener tcpListener)
    {
        Socket socket = null;
        var clientConnected = new ManualResetEvent(false);
        clientConnected.Reset();
        tcpListener.BeginAcceptSocket(delegate(IAsyncResult asyncResult)
        {
            try
            {
                socket = tcpListener.EndAcceptSocket(asyncResult);
            }
            catch (ObjectDisposedException)
            { }
            clientConnected.Set();
        }, null);
        clientConnected.WaitOne();
        return socket;
    }
}

【讨论】:

    【解决方案2】:

    这行得通,但它非常草率和浪费线程。谁能给我指出一种抛出“AcceptSocket”不会自动捕获的异常的方法?

    ChannelServer.ListeningThread = new Thread(new ThreadStart(delegate()
    {
        Log.Inform("Waiting for clients on thread {0}.", Thread.CurrentThread.ManagedThreadId);
    
        while (true)
        {
            try
            {
                ChannelServer.ClientConnected.Reset();
                ChannelServer.Listener.BeginAcceptSocket(new AsyncCallback(ChannelClientHandler.EndAcceptSocket), ChannelServer.Listener);
                ChannelServer.ClientConnected.WaitOne();
            }
            catch (ThreadInterruptedException)
            {
                Log.Inform("Interrupted client listening thread {0}.", Thread.CurrentThread.ManagedThreadId);
                break;
            }
        }
    }));
    ChannelServer.ListeningThread.Start();
    

    【讨论】:

      【解决方案3】:

      我不确定您为什么会收到该错误,但这是一个有效的简单示例:

      ManualResetEvent mrse = new ManualResetEvent(false);
      Thread test = new Thread(() =>
          {
              while (true)
              {
                  try
                  {
                      mrse.WaitOne();
                  }
                  catch (ThreadAbortException)
                  {
                      Console.WriteLine("No problem here...");
                  }
              }
          });
      
      test.IsBackground = true;
      test.Start();
      
      Thread.Sleep(1000);
      test.Abort();
      Console.ReadKey();
      

      所以它对我有用...我假设你已经通过调试器并且你在 catch 语句中的断点没有被命中,对吗?

      注意:调用Abort 是不好的做法,您应该调用Interrupt 并处理ThreadInterruptedException...这样更安全。

      【讨论】:

      • “所以它对我有用......我假设你已经通过调试器并且你在 catch 语句中的断点没有被命中,对吗?”正确。
      • @Lazo,你在哪里拨打中止电话?
      • @Lirik:来自另一个线程,它使用此方法:''ChannelServer.ListeningThread.Abort();'' 它来自另一个线程的事实是否会阻碍进程?如果是这样,我想我会使用委托。
      • 我怀疑 Accept 正在捕获与线程相关的异常并执行自己的簿记。
      • 根据 MSDN 页面,只需更改您的 while 循环以将现有主体放在 if (Pending) 中(可能与执行 sleep(0) 或 sleep(1 ) 或 sleep(500) 或其他东西)。然后你可以将'true'更改为'canceled == false'(取消是您添加的某个实例或静态布尔值)然后另一个线程可以取消= true 而不必 Abort() :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多