简体   繁体   English

C#线程模式 - 这是一个好主意吗?

[英]C# Threading Patterns - is this a good idea?

I was playing with a project of mine today and found an interesting little snippet, given the following pattern, you can safely cleanup a thread, even if it's forced to close early. 今天我正在玩我的一个项目并找到一个有趣的小片段,给定以下模式,你可以安全地清理一个线程,即使它被迫提前关闭。 My project is a network server where it spawns a new thread for each client. 我的项目是一个网络服务器,它为每个客户端生成一个新线程。 I've found this useful for early termination from the remote side, but also from the local side (I can just call .Abort() from inside my processing code). 我发现这对于从远程端提前终止是有用的,但也可以从本地端(我可以从我的处理代码中调用.Abort() )。

Are there any problems you can see with this, or any suggestions you'd make to anyone looking at a similar approach? 你有什么问题可以看到,或者你对任何看过类似方法的人提出的建议是什么?

Test case follows: 测试用例如下:


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

For reference, without the try/finally block, the thread just dies as one would expect. 作为参考,没有try / finally块,线程就像人们期望的那样死掉。

Aborting another thread at all is just a bad idea unless the whole application is coming down. 除非整个应用程序崩溃,否则完全中止另一个线程只是一个坏主意。 It's too easy to leave your program in an unknown state. 将程序置于未知状态太容易了。 Aborting your own thread is occasionally useful - ASP.NET throws a ThreadAbortException if you want to prematurely end the response, for example - but it's not a terribly nice design. 中止你自己的线程偶尔会有用 - 例如,如果你想过早地结束响应,ASP.NET会抛出一个ThreadAbortException - 但它并不是一个非常好的设计。

Safe clean-up of a thread should be mutual - there should be some shared flag requesting that the thread shuts down. 安全清理线程应该是相互的 - 应该有一些共享标志请求线程关闭。 The thread should check that flag periodically and quit appropriately. 线程应定期检查该标志并适当退出。

Whether or not this will "safely" cleanup a thread cannot be discerned from a general code sample unfortunately. 不幸的是,无法从通用代码示例中辨别出是否“安全地”清除线程。 It's highly dependent upon the actual code that is executed within the thread. 它高度依赖于线程中执行的实际代码。 There are multiple issues you must consider. 您必须考虑多个问题。 Each represents a potential bug in the code. 每个代表代码中的潜在错误。

  1. If the thread is currently in native code, it will not immediately respect the Thread.Abort call. 如果该线程当前是本机代码,它将不会立即尊重Thread.Abort调用。 It will do all of the work it wants to do in native code and will not throw until the code returns back to managed. 它将完成它想要在本机代码中完成的所有工作,并且在代码返回到托管之前不会抛出。 Until this happens thread2 will hang. 直到发生这种情况,thread2才会挂起。
  2. Any native resources that are not freed in a finally block will be leaked in this scenario. 在这种情况下,在finally块中未释放的任何本机资源都将泄露。 All native resources should be freed in a finally block but not all code does this and it's an issue to consider. 应该在finally块中释放所有本机资源,但并非所有代码都执行此操作,这是一个需要考虑的问题。
  3. Any locks that are not freed in a finally block will remain in a lock'd state and can lead to future dead locks. 在finally块中未释放的任何锁定将保持锁定状态,并可能导致将来死锁。

There are other issues which are slipping my mind at the moment. 目前还有其他一些问题让我不知所措。 But hopefully this will give you some guidance with your application. 但希望这会为您的应用程序提供一些指导。

It is generally not a good idea to abort threads. 中止线程通常不是一个好主意。 What you can do is poll for a stopRequested flag which can be set from other threads. 你可以做的是轮询一个stopRequested标志,可以从其他线程设置。 Below is a sample WorkerThread class for your reference. 下面是一个示例WorkerThread类供您参考。 For more information on how to use it, please refer to http://devpinoy.org/blogs/jakelite/archive/2008/12/20/threading-patterns-the-worker-thread-pattern.aspx 有关如何使用它的更多信息,请参阅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();
    }        
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM