zoukankan      html  css  js  c++  java
  • c# 变量,对象,静态类型,集合类的线程安全回顾

       1.变量的线程安全性与变量的作用域有关。

       2.对象 

         对象是类型的实例

         在创建对象时,会单独有内存区域存储对象的属性和方法。所以,一个类型的多个实例,在执行时,只要没有静态变量的参与,应该都是线程安全的。

    这跟我们调试状态下,是不一样的。调试状态下,如果多个线程都创建某实例的对象,每个对象都调用自身方法,在调试是,会发现是访问的同一个代码,多个线程是有冲突的。但是,真正的运行环境是线程安全的。

          以销售员为例,假设产品是充足的,多个销售员,销售产品,调用方法:Sale(),其是线程安全的。

          但是,如果涉及到仓库,必须仓库有足够的产品才能进行销售,这时,多个销售人员就有了临界资源:仓库。

          在这里我们只讨论对象的普通方法。至于方法传入的参数,以及方法内对静态变量操作的,这里需要根据参数和静态变量来判定方法的线程安全性。

          销售员案例:

    复制代码
    using System;
    using System.Threading;
    
    namespace MutiThreadSample.Sale
    {
        /// <summary>
        /// 销售
        /// </summary>
        public class Saler
        {
            /// <summary>
            /// 名称
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 间隔时间
            /// </summary>
            public int IntervalTime { get; set; }
            /// <summary>
            /// 单位时间销售运量
            /// </summary>
            public int SaleAmount { get; set; }
            /// <summary>
            /// 销售
            /// </summary>
            public void Sale()
            {
                Console.WriteLine("销售:{0} 于 {1} 销售产品 {2}", this.Name, DateTime.Now.Millisecond, this.SaleAmount);
                Thread.Sleep(IntervalTime);
            }
            /// <summary>
            /// 销售
            /// </summary>
            /// <param name="interval">时间间隔</param>
            public void Sale(object obj)
            {
                WHouseThreadParameter parameter = obj as WHouseThreadParameter;
                if (parameter != null)
                {
                    while (parameter.WHouse != null && parameter.WHouse.CanOut(this.SaleAmount))
                    {
                        parameter.WHouse.Outgoing(this.SaleAmount);
                        Console.WriteLine("Thread{0}, 销售:{1} 于 {2} 销售出库产品 {3}", Thread.CurrentThread.Name, this.Name, DateTime.Now.Millisecond, this.SaleAmount);
                        Thread.Sleep(this.IntervalTime);
                    }
                }
            }
         }
    }
    复制代码

          3静态类型

          已经讲了类的实例--对象的多线程安全性问题。这里只讨论类型的静态变量和静态方法。

          当静态类被访问的时候,CLR会调用类的静态构造器(类型构造器),创建静态类的类型对象,CLR希望确保每个应用程序域内只执行一次类型构造器,为了做到这一点,在调用类型构造器时,CLR会为静态类加一个互斥的线程同步锁,因此,如果多个线程试图同时调用某个类型的静态构造器时,那么只有一个线程可以获得对静态类的访问权,其他的线程都被阻塞。第一个线程执行完 类型构造器的代码并释放构造器之后,其他阻塞的线程被唤醒,然后发现构造器被执行过,因此,这些线程不再执行构造器,只是从构造器简单的返回。如果再一次调用这些方法,CLR就会意识到类型构造器被执行过,从而不会在被调用。

          调用类中的静态方法,或者访问类中的静态成员变量,过程同上,所以说静态类是线程安全的。

          最简单的例子,就是数据库操作帮助类。这个类的方法和属性是线程安全的。

    复制代码
    using System;
    
    namespace MutiThreadSample.Static
    {
        public class SqlHelper
        {        
            /// <summary>
            /// 数据库连接
            /// </summary>
            private static readonly string ConnectionString = "";
            /// <summary>
            /// 执行数据库命令
            /// </summary>
            /// <param name="sql">SQL语句</param>
            public static void ExcuteNonQuery(string sql)
            { 
                //执行数据操作,比如新增、编辑、删除
            }
        }
    }
    复制代码

           但是,对于静态变量其线程安全性是相对的,如果多个线程来修改静态变量,这就不一定是线程安全的。而静态方法的线程安全性,直接跟传入的参数有关。

           总之:

          针对变量、对象、类型,说线程安全性,比较笼统,在这里,主要是想让大家明白,哪些地方需要注意线程安全性。对于变量、对象(属性、方法)、静态变量、静态方法,其线程安全性是相对的,需要根据实际情况而定。

         万剑不离其宗,其判定标准:是否有临界资源。

     4、集合类型是线程安全的吗?

          常用的集合类型有List、Dictionary、HashTable、HashMap等。在编码中,集合应用很广泛中,常用集合来自定义Cache,这时候必须考虑线程同步问题。 

          默认情况下集合不是线程安全的。在System.Collections 命名空间中只有几个类提供Synchronize方法,该方法能够超越集合创建线程安全包装。但是,System.Collections命名空间中的所有类都提供SyncRoot属性,可供派生类创建自己的线程安全包装。还提供了IsSynchronized属性以确定集合是否是线程安全的。但是ICollection泛型接口中不提供同步功能,非泛型接口支持这个功能。

         Dictionary(MSDN解释)

         此类型的公共静态(在 Visual Basic 中为 Shared)成员是线程安全的。 但不保证所有实例成员都是线程安全的。
         只要不修改该集合,Dictionary<TKey, TValue> 就可以同时支持多个阅读器。 即便如此,从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。 当出现枚举与写访问互相争用这种极少发生的情况时,必须在整个枚举过程中锁定集合。 若允许多个线程对集合执行读写操作,您必须实现自己的同步。

          很多集合类型都和Dictionary类似。默认情况下是线程不安全的。当然微软也提供了线程安全的Hashtable.

          HashTable 

          Hashtable 是线程安全的,可由多个读取器线程和一个写入线程使用。 多线程使用时,如果只有一个线程执行写入(更新)操作,则它是线程安全的,从而允许进行无锁定的读取(若编写器序列化为 Hashtable)。 若要支持多个编写器,如果没有任何线程在读取 Hashtable 对象,则对 Hashtable 的所有操作都必须通过 Synchronized 方法返回的包装完成。

          从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。 即使某个集合已同步,其他线程仍可以修改该集合,这会导致枚举数引发异常。 若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。

         线程安全起见请使用以下方法声明

            /// <summary>
            ///  Syncronized方法用来创造一个新的对象的线程安全包装
            /// </summary>
            private Hashtable hashtable = Hashtable.Synchronized(new Hashtable());

        在枚举读取时,加lock,这里lock其同步对象SyncRoot 

    复制代码
            /// <summary>
            /// 读取
            /// </summary>
            public void Read()
            { 
                lock(hashtable.SyncRoot)
                {
                    foreach (var item in hashtable.Keys)
                    {
                        Console.WriteLine("Key:{0}",item);
                    }
                }
            }
    复制代码
  • 相关阅读:
    Elasticsearch Transport 模块创建及启动分析
    ElasticSearch中的JVM性能调优
    ElasticSearch 线程池类型分析之 ResizableBlockingQueue
    ElasticSearch 线程池类型分析之 ExecutorScalingQueue
    ElasticSearch 线程池类型分析之SizeBlockingQueue
    Redis的LRU算法
    hdu 1828 Picture(线段树扫描线矩形周长并)
    sublime text2 插件
    Python学习笔记(十四):模块高级
    梦想启航
  • 原文地址:https://www.cnblogs.com/bile/p/6114506.html
Copyright © 2011-2022 走看看