4

我正在寻找 C# 中的线程安全代码,如果可能的话,它将避免两个问题;

  1. 如果线程对象在任何时候被处理掉,线程循环就会很好地退出。
  2. 当被另一个类或表单(尤其是表单)使用时,使用该类的开发人员不必记住挂钩表单的关闭事件并调用 dispose。例如服务器线程的正确完成

线程正在调用具有以下 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() 之间调用了处理。这有多糟糕?
  • 有什么方法可以包装这个类,这样它的用户就不必记住调用 dispose. 目前,如果他们不这样做,应用程序就坐在那里等待线程完成,而它永远不会。将其设置为后台线程似乎很懒惰。
4

1 回答 1

1

即使我正在处理我的班级,我的活动也可以被调用,因为线程必须完成推出。假设在 try/catch 和 syncContext.Post() 之间调用了处理。这有多糟糕?

它看起来没那么糟糕。一旦你设置Finished=true. 它应该完成发布新委托,然后退出循环。但是,您可能会遇到 CPU 缓存问题Finished。运行循环的 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();
}

(我会放弃终结器作为一个选项,因为不知道它是否/何时运行。)

于 2012-09-07T08:34:44.447 回答