在C#中确保静态方法的线程安全性

5

我有一些代码目前在静态类/方法中,但我想确认它是否是线程安全的。从我所读的内容来看,我认为这应该没问题,但我脑海中的某些东西说它可能不是。我的网页数据处理阶段使用外部网络服务创建订单记录,这可能会很慢:可能需要30-40秒,也可能需要5到10分钟(这超出了我的控制范围),因此我将返回一个页面给用户,然后启动一个新线程,等处理完成后再向用户发送电子邮件。这目前在静态类/方法中。只要我的所有对象都在特定方法内创建(除了系统默认值,这将是共同的),那么该方法应该是线程安全的,对吗?例如,如果我有

public static class ProcessOrder()
{
    public static int GetOrderMaxSize()
    {
        return (....gets and parses ConfigurationManager.AppSettings["MaxOrderSize"]...);
    }

    public static bool CreateOrder(Order order)
    {
        XmlDocument xmlDoc = GetOrderXML(order);
        bool check = false;
        using (CreateOrderXML.Create xmlCo = new CreateOrderXML.Create())
        {
            xmlCo.Timeout = 60000;
            System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();

            string xmlString = "";
            using (StringWriter stringWriter = new StringWriter())
            {
                using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter))
                {
                    xmlDoc.WriteTo(xmlWriter);
                    xmlWriter.Flush();
                    xmlString = stringWriter.GetStringBuilder().ToString();
                }
            }

            byte[] bXMLOrder = encoding.GetBytes(xmlString);
            byte[] breturnMessage;

            check = xmlCo.Create(bXMLOrder, out breturnMessage);
            .... do something with return message
        }
        return check;
    }

    private static XmlDocument GetOrderXML(Order order)
    {
        ... creates an XML object for the order
    }
}

(CreateOrderXML是指向Web Service URL/method的服务引用)对于长时间运行的并发线程,特别是在xmlCo.Create(...)阶段,这会是线程安全的吗?我知道如果我开始在类成员中放置对象并在方法中使用它们,这肯定会导致不同线程覆盖值的问题,但只要对象在方法内创建,它们应该没问题,对吗?
2个回答

13

看起来您并没有访问任何共享数据;您正在请求远程资源,并且每次执行此方法时都构建唯一的数据集。因此,那里不需要同步。

这里的每个方法执行都会创建本地变量-它自己的副本。因此,永远没有任何内容被共享。


是的,我在类本身中故意只使用了“系统范围内默认”的变量,并通过方法参数传递所有需要处理的可能特定于用户/会话的内容。但我仍然有警告信号在我的脑海里响起,希望它们只是“健康的偏执狂”。 - Mad Halfling
听起来对我来说是健康的多疑心态! - Andrew Barber

5
如果你的静态方法没有访问任何静态(类)数据,那么它应该是线程安全的。唯一可能存在争用点的是它可能使用的外部资源(例如文件或其他系统资源),以及传入的数据。只有您知道这种使用的上下文。
像这样可能存在争用的东西的使用可以使用锁定或其他原语进行序列化。不要忘记按相同顺序序列化资源,以免死锁。如果您有一个使用资源A和B的方法:
lock( latch_a )
{
   process(object_a) ;
   lock ( latch_b )
   {
     process(object_a,object_b) ;
   }
}

还有另一种方法可以反转:

lock( latch_b )
{
   process(object_b) ;
   lock ( latch_a )
   {
     process(object_a,object_b) ;
   }
}

在某个时刻,你的两个线程将会死锁,当它们中的每一个需要一个资源,而另一个需要访问该资源之前才能放弃对该资源的访问权限时。 编辑说明: 有关详细信息,请参阅C#文档中的lock语句。一般来说,被锁定的对象代表(并可能是)正在进行序列化的共享资源访问。一个常见的模式是执行以下操作:
class Widget
{
   private static readonly object X = new object() ;

   public void Foo()
   {
     lock(X)
     {
       // Do work using shared resource
     }
     return ;
   }

}

latch_a和latch_b是需要创建一次,然后在每次调用静态方法时传入的对象吗? - Doug
@Doug:请查看我的修改后的答案。 - Nicholas Carey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接