处理后完成一个线程循环

时间:2012-09-07 05:22:06

标签: c# thread-safety dispose

我在C#中寻找可以避免两个问题的线程安全代码;

  1. 如果线程对象在任何时候被丢弃,则线程循环很好地退出。
  2. 当被其他类或表单(特别是表单)使用时,使用该类的开发人员不必记住挂钩表单的close事件并调用dispose。例如。 Correct finishing of server's thread
  3. 线程正在调用一个具有以下while循环的方法;

    while (!Finished)
    {
        string request = "";
        try
        {
            //Block until we have a message or we are terminated in dispose **
            request = server.Recv(Encoding.Unicode);  
            //Send a response to the sender so they know we received. 
            server.Send(request, Encoding.Unicode);
        }
        catch (Exception e)
        {
            //Catch a termination error. 
            if (e.Errno == ETERM)
            {
                break;
            }
        }
        syncContext.Post(
                         new SendOrPostCallback(delegate(object state)
                         {
                             MessageHandler handler = OnMessage;
                             if (handler != null)
                             {
                                 handler(request);
                             }
                         }), null);
    }
    

    目前处理的情况如此;

    public void Dispose()
    {
        Finished = true;
        //Cause all blocking message requests and sends to terminate with exception ** 
        FZMQConext.Dispose();
    
        //Wait for the thread to finish up.
        FThread.Join();
        GC.SuppressFinalize(this);
    }
    

    我认为是问题;

    • 我的事件可以被调用,即使我正在处理我的类,因为线程必须完成推出。在try / catch和syncContext.Post()之间调用dispos。这有多糟糕?
    • 有没有办法结束这个课程,所以它的用户不必记得调用dispose。目前,如果他们没有应用程序坐在那里等待线程完成,它永远不会。将它设置为后台线程似乎很懒惰。

1 个答案:

答案 0 :(得分:1)

  

我的事件可以被调用,即使我正在处理我的类,因为线程必须完成推出。在try / catch和syncContext.Post()之间调用dispos。这有多糟糕?

看起来并不那么糟糕。设置Finished=true后。它应该完成发布一个新的委托,然后退出循环。但是,您可能会遇到Finished的CPU缓存问题。 另一个线程更改了值后,运行循环的CPU核心可以读取Finished 缓存值,因此循环可能会错误地继续运行。为了防止这种情况,您应该创建一个仅通过Interlocked修改的私有字段,以及一个修改该字段的方法:

private int _running = 1;
public void Stop()
{
    Interlocked.Exchange(ref _running, 0);
}

并且循环将运行while( _running > 0)

  

有没有办法包装这个类,所以它的用户不必记得调用dispose。目前,如果他们没有应用程序坐在那里等待线程完成,它永远不会。将它设置为后台线程似乎很懒惰。

实际上没有一般方法可以避免将处理的负担放在客户端上。但是如果这个类总是被Winforms组件使用,那么你可以编写一个构造函数来获取父组件并订阅父组件的Disposed事件。因此,当处理父组件(例如表单)时,该对象也会被处置:

public MyClass(Component parent)
{
    parent.Disposed += (s,e) => this.Dispose();
}

(我会把终结器作为一个选项扔出去,因为没有必要知道它是否/什么时候会运行。)