zoukankan      html  css  js  c++  java
  • 多线程中Local Store Slot(本地存储槽)

    在Java中有一种ThreadLocal机制,为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。比如在Hibernate中使用Session的时候,因为Session是线程不安全的,所以要考虑并发问题。而使用ThreadLocal的话,会在每个线程中有一个Session的副本,所以就不会有线程冲突的问题。

    .NET中也有相应的机制,来实现变量的线程局部化,而且有多种方法

    1. 使用ThreadStatic特性

    ThreadStatic特性是最简单的TLS使用,且只支持静态字段,只需要在字段上标记这个特性就可以了:

    //TLS中的str变量
    [ThreadStatic]
    static string str = "hehe";
     
    static void Main()
    {
        //另一个线程只会修改自己TLS中的str变量
        Thread th = new Thread(() => { str = "Mgen"; Display(); });
        th.Start();
        th.Join();
        Display();
    }
     
    static void Display()
    {
        Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, str);
    }

    运行结果:

    1 hehe
    3 Mgen

     可以看到,str静态字段在两个线程中都是独立存储的,互相不会被修改。


    2. 使用命名的LocalDataStoreSlot类型

    显然ThreadStatic特性只支持静态字段太受限制了。.NET线程类型中的LocalDataStoreSlot提供更好的TLS支持。我们先来看看命名的LocalDataStoreSlot类型,可以通过Thread.AllocateNamedDataSlot来分配一个命名的空间,通过Thread.FreeNamedDataSlot来销毁一个命名的空间。空间数据的获取和设置则通过Thread类型的GetData方法和SetData方法。

    static void Main()
    {
        //创建Slot
        LocalDataStoreSlot slot = Thread.AllocateNamedDataSlot("slot");
        //设置TLS中的值
        Thread.SetData(slot, "hehe");
        //修改TLS的线程
        Thread th = new Thread(() =>
            {
                Thread.SetData(slot, "Mgen");
                Display();
            });
     
        th.Start();
        th.Join();
        Display();
        //清除Slot
        Thread.FreeNamedDataSlot("slot");
    }
     
    //显示TLS中Slot值
    static void Display()
    {
        LocalDataStoreSlot dataslot = Thread.GetNamedDataSlot("slot");
        Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(dataslot));
    }

    输出:

    3 Mgen
    1 hehe


    3. 使用未命名的LocalDataStoreSlot类型

    线程同样支持未命名的LocalDataStoreSlot,未命名的LocalDataStoreSlot不需要手动清除,分配则需要Thread.AllocateDataSlot方法。注意由于未命名的LocalDataStoreSlot没有名称,因此无法使用Thread.GetNamedDataSlot方法,只能在多个线程中引用同一个LocalDataStoreSlot才可以对TLS空间进行操作,将上面的命名的LocalDataStoreSlot代码改成未命名的LocalDataStoreSlot执行:

    //静态LocalDataStoreSlot变量
    static LocalDataStoreSlot slot;
     
    static void Main()
    {
        //创建Slot
        slot = Thread.AllocateDataSlot();
        //设置TLS中的值
        Thread.SetData(slot, "hehe");
        //修改TLS的线程
        Thread th = new Thread(() =>
            {
                Thread.SetData(slot, "Mgen");
                Display();
            });
     
        th.Start();
        th.Join();
        Display();
    }
     
    //显示TLS中Slot值
    static void Display()
    {
        Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, Thread.GetData(slot));
    }
     


    4. 使用.NET 4.0的ThreadLocal<T>类型

    .NET 4.0在线程方面加入了很多东西,其中就包括ThreadLocal<T>类型,他的出现更大的简化了TLS的操作。ThreadLocal<T>类型和Lazy<T>惊人相似,构造函数参数是Func<T>用来创建对象(当然也可以理解成对象的默认值),然后用Value属性来得到或者设置这个对象。ThreadLocal的操作或多或少有点像上面的未命名的LocalDataStoreSlot,但ThreadLocal感觉更简洁更好理解。

    static ThreadLocal<string> local;
     
    static void Main()
    {
        //创建ThreadLocal并提供默认值
        local = new ThreadLocal<string>(() => "hehe");
        //修改TLS的线程
        Thread th = new Thread(() =>
            {
                local.Value = "Mgen";
                Display();
            });
     
        th.Start();
        th.Join();
        Display();
    }
     
    //显示TLS中数据值
    static void Display()
    {
        Console.WriteLine("{0} {1}", Thread.CurrentThread.ManagedThreadId, local.Value);
    }


    输出:

    3 Mgen

    1 hehe 

    5. 强调一下不同方法和TLS的默认值
    上面代码都是一个一个线程设置值,另一个线程直接修改值然后输出,不会觉察到TLS中默认值的状况,下面专门强调一下不同方法的默认值状况。ThreadStatic不提供默认值:

    [ThreadStatic]
    static int i = 123;
     
    static void Main()
    {
        //输出本地线程TLS数据值
        Console.WriteLine(i);
        //输出另一个线程TLS数据值
        ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(i));
        //控制台等待线程结束
        Console.ReadKey();
    }

    输出:
    123

    0

    显然本地线程TLS数据时123,而静态变量的默认值不会在另一个线程中初始化的。

    LocalDataStoreSlot很容易可以看出来,不可能有默认值,因为初始化只能构造一个空间,而不能赋予它值,Thread.SetData显然只会在TLS中设置数据,还是用代码演示一下:

    static LocalDataStoreSlot slot = Thread.AllocateDataSlot();
     
    static void Main()
    {
        Thread.SetData(slot, 123);
        //输出本地线程TLS数据值
        Console.WriteLine(Thread.GetData(slot));
        //输出另一个线程TLS数据值
        ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(Thread.GetData(slot) == null));
        //控制台等待线程结束
        Console.ReadKey();
    }


    输出:
    123
    True
    第二行是True,那么另一个线程中的数据是null。

    最后重点:.NET 4.0后的ThreadLocal会提供默认值的,还记得我上面说的那句话“ThreadLocal的操作或多或少有点像上面的未命名的LocalDataStoreSlot”?有人可能会问那为什么要创造出ThreadLocal?还有一个很大的区别ThreadLocal可以提供TLS中数据的默认值。(另外还有ThreadLocal是泛型类,而LocalDataStoreSlot不是)。

    static ThreadLocal<int> local = new ThreadLocal<int>(() => 123);
    static void Main()
    {
        //输出本地线程TLS数据值
        Console.WriteLine(local.Value);
        //输出另一个线程TLS数据值
        ThreadPool.QueueUserWorkItem(_ => Console.WriteLine(local.Value));
        //控制台等待线程结束
        Console.ReadKey();
    }

    输出:
    123
    123

  • 相关阅读:
    Windows 8实例教程系列 开篇
    qt 开发发布于 windeploy.exe
    qt qoci 测试验证
    vmware vmx 版本不兼容
    qt oracle
    vc qt dll
    QOCIDriver unable to create environment
    qoci 编译完 放置位置 具体根据情况
    calling 'lastError' with incomplete return type 'QSqlError' qsqlquer
    Hbase 操作工具类
  • 原文地址:https://www.cnblogs.com/james1207/p/3320245.html
Copyright © 2011-2022 走看看