C # Threading Patterns - это хорошая идея? - PullRequest
3 голосов
/ 28 февраля 2009

Сегодня я играл с моим проектом и нашел интересный небольшой фрагмент, учитывая следующий шаблон, вы можете безопасно очистить поток, даже если он вынужден закрываться рано. Мой проект - это сетевой сервер, на котором создается новый поток для каждого клиента. Я нашел это полезным для раннего завершения с удаленной стороны, но также и с локальной стороны (я могу просто позвонить .Abort() из моего кода обработки).

Есть ли какие-либо проблемы, с которыми вы можете столкнуться, или какие-либо предложения, которые вы могли бы сделать для тех, кто рассматривает подобный подход?

Контрольный пример следующий:


using System;
using System.Threading;

class Program
{
    static Thread t1 = new Thread(thread1);
    static Thread t2 = new Thread(thread2);

    public static void Main(string[] args)
    {
        t1.Start();
        t2.Start();
        t1.Join();
    }

    public static void thread1() {
        try {
            // Do our work here, for this test just look busy.
            while(true) {
                Thread.Sleep(100);
            }
        } finally {
            Console.WriteLine("We're exiting thread1 cleanly.\n");
            // Do any cleanup that might be needed here.
        }
    }

    public static void thread2() {
        Thread.Sleep(500);
        t1.Abort();
    }
}

Для справки, без блока try / finally поток просто умирает, как и следовало ожидать.

Ответы [ 3 ]

14 голосов
/ 28 февраля 2009

Отмена другого потока вообще является плохой идеей, если все приложение не закрывается. Слишком легко оставить вашу программу в неизвестном состоянии. Иногда полезно отменить ваш собственный поток - ASP.NET выдает ThreadAbortException, например, если вы хотите преждевременно завершить ответ, - но это не очень приятный дизайн.

Безопасная очистка потока должна быть взаимной - должен быть какой-то общий флаг , запрашивающий , что поток отключается. Поток должен периодически проверять этот флаг и выходить соответствующим образом.

3 голосов
/ 28 февраля 2009

Будет ли это "безопасно" очищать поток, к сожалению, из общего примера кода невозможно определить. Это сильно зависит от фактического кода, который выполняется в потоке. Есть несколько вопросов, которые вы должны рассмотреть. Каждый представляет потенциальную ошибку в коде.

  1. Если поток в настоящее время находится в собственном коде, он не будет немедленно учитывать вызов Thread.Abort. Он выполнит всю работу, которую он хочет выполнить в нативном коде, и не сгенерирует, пока код не вернется обратно к управляемому. Пока это не произойдет, thread2 будет зависать.
  2. В этом сценарии будут пропущены все собственные ресурсы, которые не освобождены в блоке finally. Все собственные ресурсы должны быть освобождены в блоке finally, но не весь код делает это, и это проблема для рассмотрения.
  3. Любые блокировки, которые не были освобождены в блоке finally, останутся в состоянии блокировки и могут привести к будущим мертвым блокировкам.

Есть и другие проблемы, которые в настоящее время ускользают от меня. Но, надеюсь, это даст вам некоторое представление о вашем приложении.

0 голосов
/ 18 марта 2009

Прекращение потоков обычно не очень хорошая идея. Что вы можете сделать, это опросить флаг stopRequested, который может быть установлен из других потоков. Ниже приведен пример класса WorkerThread для вашей справки. Для получения дополнительной информации о том, как его использовать, пожалуйста, обратитесь к http://devpinoy.org/blogs/jakelite/archive/2008/12/20/threading-patterns-the-worker-thread-pattern.aspx

public abstract class WorkerThreadBase : IDisposable
{
    private Thread _workerThread;
    protected internal ManualResetEvent _stopping;
    protected internal ManualResetEvent _stopped;
    private bool _disposed;
    private bool _disposing;
    private string _name;

    protected WorkerThreadBase()
        : this(null, ThreadPriority.Normal)
    {
    }

    protected WorkerThreadBase(string name)
        : this(name, ThreadPriority.Normal)
    {
    }

    protected WorkerThreadBase(string name,
        ThreadPriority priority)
        : this(name, priority, false)
    {
    }

    protected WorkerThreadBase(string name,
        ThreadPriority priority,
        bool isBackground)
    {
        _disposing = false;
        _disposed = false;
        _stopping = new ManualResetEvent(false);
        _stopped = new ManualResetEvent(false);

        _name = name == null ? GetType().Name : name; ;
        _workerThread = new Thread(threadProc);
        _workerThread.Name = _name;
        _workerThread.Priority = priority;
        _workerThread.IsBackground = isBackground;
    }

    protected bool StopRequested
    {
        get { return _stopping.WaitOne(1, true); }
    }

    protected bool Disposing
    {
        get { return _disposing; }
    }

    protected bool Disposed
    {
        get { return _disposed; }
    }

    public string Name
    {
        get { return _name; }            
    }   

    public void Start()
    {
        ThrowIfDisposedOrDisposing();
        _workerThread.Start();
    }

    public void Stop()
    {
        ThrowIfDisposedOrDisposing();
        _stopping.Set();
        _stopped.WaitOne();
    }

    public void WaitForExit()
    {
        ThrowIfDisposedOrDisposing();            
        _stopped.WaitOne();
    }

    #region IDisposable Members

    public void Dispose()
    {
        dispose(true);
    }

    #endregion

    public static void WaitAll(params WorkerThreadBase[] threads)
    { 
        WaitHandle.WaitAll(
            Array.ConvertAll<WorkerThreadBase, WaitHandle>(
                threads,
                delegate(WorkerThreadBase workerThread)
                { return workerThread._stopped; }));
    }

    public static void WaitAny(params WorkerThreadBase[] threads)
    {
        WaitHandle.WaitAny(
            Array.ConvertAll<WorkerThreadBase, WaitHandle>(
                threads,
                delegate(WorkerThreadBase workerThread)
                { return workerThread._stopped; }));
    }

    protected virtual void Dispose(bool disposing)
    {
        //stop the thread;
        Stop();

        //make sure the thread joins the main thread
        _workerThread.Join(1000);

        //dispose of the waithandles
        DisposeWaitHandle(_stopping);
        DisposeWaitHandle(_stopped);
    }

    protected void ThrowIfDisposedOrDisposing()
    {
        if (_disposing)
        {
            throw new InvalidOperationException(
                Properties.Resources.ERROR_OBJECT_DISPOSING);
        }

        if (_disposed)
        {
            throw new ObjectDisposedException(
                GetType().Name,
                Properties.Resources.ERROR_OBJECT_DISPOSED);
        }
    }

    protected void DisposeWaitHandle(WaitHandle waitHandle)
    {
        if (waitHandle != null)
        {
            waitHandle.Close();
            waitHandle = null;
        }
    }

    protected abstract void Work();

    private void dispose(bool disposing)
    {
        //do nothing if disposed more than once
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
            _disposing = disposing;

            Dispose(disposing);

            _disposing = false;
            //mark as disposed
            _disposed = true;
        }
    }

    private void threadProc()
    {
        Work();
        _stopped.Set();
    }        
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...