如何使静态方法线程安全?

时间:2013-06-07 05:09:00

标签: c# multithreading static-methods

我编写了一个静态类,它是我从不同类调用的一些函数的存储库。

public static class CommonStructures
{
    public struct SendMailParameters
    {
        public string To { get; set; }
        public string From { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }
        public string Attachment { get; set; }
    }
}

public static class CommonFunctions
{
    private static readonly object LockObj = new object();
    public static bool SendMail(SendMailParameters sendMailParam)
    {
        lock (LockObj)
        {
            try
            {
                //send mail
                return true;
            }
            catch (Exception ex)
            {
                //some exception handling
                return false;
            }
        }
    }

    private static readonly object LockObjCommonFunction2 = new object();
    public static int CommonFunction2(int i)
    {
        lock (LockObjCommonFunction2)
        {
            int returnValue = 0;
            try
            {
                //send operation
                return returnValue;
            }
            catch (Exception ex)
            {
                //some exception handling
                return returnValue;
            }
        }
    }
}

问题1:对于我的第二个方法CommonFunction2,在这个例子中我是否使用新的静态锁,即LockObjCommonFunction2,或者我可以重用在函数开头定义的相同锁定对象LockObj。

问题2:是否存在可能导致线程相关问题的任何问题,或者我可以将代码改进为安全线程。

问题3:在传递公共类而不是结构中是否存在任何问题..在这个示例中,SendMailParameters(我使用包装所有参数,而不是将多个参数包含在SendMail函数中)?

此致 MH

4 个答案:

答案 0 :(得分:5)

  

问题1:对于我的第二个方法CommonFunction2,我是否使用新方法   静态锁,即本例中的LockObjCommonFunction2,或者我可以重用   在函数开头定义的锁对象LockObj。

如果要同步这两种方法,则需要对它们使用相同的锁。例如,如果thread1正在访问您的Method1,并且thread2正在访问您的Method2,并且您希望它们不能同时访问两个内部,使用相同的锁。但是,如果您只想限制对 方法1或2的并发访问,使用不同的锁

  

问题2:是否有任何可能导致线程相关的问题   问题或者我可以将代码改进为安全线程。

永远记住共享资源(例如静态变量,文件)不是线程安全的,因为它们很容易被所有线程访问,因此您需要应用任何类型的同步(通过锁,信号,互斥等)。

  

问题3:传递普通类而不是传递问题是否存在任何问题   struct ..在这个例子中SendMailParameters(我使用它   包装所有参数,而不是具有多个参数   SendMail功能)?

只要您应用适当的同步,它就是线程安全的。对于结构体,请查看this作为参考。

底线是您需要为共享内存中的任何内容应用正确的同步。此外,您应始终注意要生成的线程的范围以及每个方法使用的变量的状态。他们是改变状态还是只依赖变量的内部状态?线程是否总是创建一个对象,尽管它是静态的/共享的?如果是,那么它应该是线程安全的。否则,如果它只是重​​用某个共享资源,那么您应该应用适当的同步。最重要的是,即使没有共享资源,死锁也可能发生,所以remember the basic rules in C# to avoid deadlocks P.S。感谢Euphoric分享Eric Lippert的文章。

但要小心你的同步。尽可能将其范围限制为仅修改共享资源的位置。因为它可能会给您的应用带来不便的瓶颈,因为性能会受到很大影响。

    static readonly object _lock = new object();
    static SomeClass sc = new SomeClass();
    static void workerMethod()
    {
        //assuming this method is called by multiple threads

        longProcessingMethod();

        modifySharedResource(sc);
    }

    static void modifySharedResource(SomeClass sc)
    {
        //do something
        lock (_lock)
        {
            //where sc is modified
        }
    }

    static void longProcessingMethod()
    {
        //a long process
    }

答案 1 :(得分:0)

您可以根据需要多次重复使用相同的锁定对象,但这意味着各个线程可以同时访问由同一个锁定所包围的所有代码区域。所以你需要做出相应的计划,并仔细。

例如,如果有多个函数编辑同一个数组,有时最好将一个锁定对象用于多个位置。其他时候,多个锁定对象更好,因为即使一段代码被锁定,另一段仍然可以运行。

多线程编码就是仔细规划......

为了保持超级安全,以牺牲可能写得慢得多的代码为代价......你可以通过锁添加一个访问器到你的静态类环绕声。这样,您可以确保两个线程不会同时调用该类的任何方法。这是非常强大的力量,绝对是专业人士的“禁忌”。但如果你只是熟悉这些东西是如何运作的,那么开始学习并不是一个糟糕的地方。

答案 2 :(得分:0)

1)首先,它取决于你想要的东西:

原样(两个独立的锁定对象) - 没有两个线程会同时执行相同的方法,但它们可以同时执行不同的方法。

如果更改为具有单个锁定对象,则没有两个线程将在共享锁定对象下执行这些部分。

2)在你的片段中,没有任何东西让我觉得错误 - 但代码并不多。如果您的存储库从自身调用方法,那么您可能会遇到问题,并且您可能会遇到一些问题:)

3)关于结构我不会使用它们。使用类更好/更容易,因为有一些问题与结构有关,你不需要那些问题。

答案 3 :(得分:0)

要使用的锁定对象的数量取决于您尝试保护的数据类型。如果在多个线程上读取/更新了多个变量,则应为每个独立变量使用单独的锁定对象。因此,如果您有10个变量形成6个独立变量组(就您打算如何读/写它们而言),您应该使用6个锁定对象以获得最佳性能。 (一个独立变量是在多个线程上读/写而不影响其他变量值的变量。如果对于给定的动作必须一起读取2个变量,它们彼此依赖,因此它们必须被锁定在一起。我希望这不会太混乱。)

锁定区域应尽可能短以获得最佳性能 - 每次锁定代码区域时,在释放锁定之前,其他任何线程都无法进入该区域。如果您有许多自变量但使用的锁定对象太少,那么您的性能将受到影响,因为您的锁定区域会变长。

拥有更多的锁定对象允许更高的并行性,因为每个线程可以读/写一个不同的独立变量 - 如果线程尝试读取/写入彼此依赖的变量,则它们只需要彼此等待(因此通过相同的锁定对象锁定。)

在你的代码中你必须小心你的SendMailParameters输入参数 - 如果这是一个引用类型(类,而不是结构),你必须确保它的属性被锁定或者它不被多个访问线程。如果它是一个引用类型,它只是一个指针,并且在其属性getter / setter中没有锁定,多个线程可能会尝试读/写同一实例的某些属性。如果发生这种情况,您的SendMail()函数可能最终使用损坏的实例。仅仅在SendMail()内部锁定是不够的 - SendMailParameters的属性和方法也必须受到保护。