Thread.Abort, похоже, не вызывает исключение ThreadAbortException из-за AcceptSocket

Я звоню ChannelServer.ListeningThread.Abort в следующем потоке, однако ничего не происходит. Я хотел бы быть более конкретным, но я не могу ничего придумать. Похоже, что ThreadAbortException не выбрасывается, и это исключение должно быть выброшено независимо от блокирующего прослушивателя (оно отлично работает с потоками, которые получают блокировку).

Важное EDIT: с ManualResetEvent.WaitOne вместо AcceptSocket, как предложил Лирик для тестирования, все работает отлично. Почему AcceptSocket блокирует ThreadAbortException?

ССЫЛКА: эта ветка форума, кажется, обсуждает ту же проблему, хотя я ничего не могу понять: 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();

person Lazlo    schedule 29.06.2010    source источник
comment
Я не могу себе представить, что это правильный способ делать то, что вы пытаетесь сделать. Вы действительно не должны порождать столько потоков.   -  person Adam Robinson    schedule 29.06.2010
comment
@ Адам, это обычный фрагмент кода на сервере ... запускает новый поток для каждого принятого соединения.   -  person Kiril    schedule 29.06.2010
comment
Ой, это ненормально ни для чего, кроме тривиального сервера. Поток на соединение обычно вызывает проблемы при масштабировании.   -  person spender    schedule 29.06.2010
comment
Это тривиальный сервер, и вопрос не в этом. :)   -  person Lazlo    schedule 29.06.2010
comment
такое же поведение, если вы измените попытку просто сделать AcceptSocket?   -  person James Manning    schedule 29.06.2010
comment
@James: Только что проверил, такое же поведение.   -  person Lazlo    schedule 29.06.2010
comment
если вы выполняете вызов .Abort(), а затем входит клиент (поэтому AcceptSocket перестает блокировать), ТОГДА он выбрасывает?   -  person James Manning    schedule 30.06.2010
comment
Кстати, поскольку поведение здесь, по-видимому, заключается в том, что вызов блокирует ядро ​​​​(а прерывания в управляемом потоке недостаточно, чтобы разблокировать его), в соответствии со страницей MSDN вы можете вместо этого зациклиться на ожидании: Если вы хотите избежать блокировки, используйте метод Pending, чтобы определить, доступны ли запросы на подключение в очереди входящих подключений.   -  person James Manning    schedule 30.06.2010


Ответы (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... это намного безопаснее.

person Kiril    schedule 29.06.2010
comment
Так что это работает для меня ... Я предположил, что вы прошли через отладчик, и ваша точка останова внутри оператора catch не сработала, верно? Правильный. - person Lazlo; 29.06.2010
comment
@Lazo, где вы делаете вызов прерывания? - person Kiril; 29.06.2010
comment
@Lirik: из другого потока, который использует этот метод: ''ChannelServer.ListeningThread.Abort();'' Мешает ли процессу тот факт, что он исходит из другого потока? Если это так, я думаю, я буду использовать делегатов. - person Lazlo; 29.06.2010
comment
@Lazlo, в моем примере это исходит из другого потока (основного потока), так что это определенно не проблема. Является ли опубликованный вами код самым простым примером, в котором вы видите проблему? Нет другого кода, который может вызывать проблемы? - person Kiril; 29.06.2010
comment
Я абсолютно уверен, что это самый прямой вызов - у меня могут быть точки останова и информация журнала до и после вызова прерывания, я абсолютно УВЕРЕН, что аборт вызывается. Я использовал очень похожий код для процедур приема, и он работает. Я действительно озадачен... - person Lazlo; 29.06.2010
comment
@Lirik: Примечание. Я только что проверил прерывание, но оно все еще не работает. Но спасибо за подсказку. :) - person Lazlo; 29.06.2010
comment
Я подозреваю, что Accept перехватывают исключения, связанные с потоками, и ведут собственный учет. - person Yuliy; 29.06.2010
comment
@Yuliy: Похоже, что да, хотя Receive on a Socket - нет, поэтому я чувствую необычное несоответствие в структуре. Возможно, мне придется сделать это с помощью встроенного асинхронного режима. методы (BeginAccept/EndAccept). - person Lazlo; 29.06.2010
comment
согласно странице MSDN, кажется проще просто изменить цикл while, чтобы поместить существующее тело в if (Pending) (возможно, с else, который делает сон (0), сон (1) или сон (500) или что-то ). Затем вы можете изменить «true» на «canceled == false» (отменено как добавленный вами экземпляр или статическое логическое значение), тогда другой поток может просто отменить = true вместо того, чтобы прерывать () :) - person James Manning; 30.06.2010

Это работает, но невероятно неаккуратно и напрасно тратит потоки. Может ли кто-нибудь просто указать мне способ создать исключение, которое «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();
person Lazlo    schedule 29.06.2010

Вот простой метод расширения 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;
    }
}
person picrap    schedule 27.03.2014