次の方法で共有


Are you familiar with [ThreadStatic]?

If you're stuffing anything in thread local storage, you might be interested in the performance comparison between Thread.SetData and [ThreadStatic].

ThreadStatic is super-cool, if you have a static variable, you can make it static "per-thread" by placing the attribute on top. This is one easy way to get around the thread-safety issues of working with statics - as they are per-thread, you do not have to take a lock when updating them.

[ThreadStatic]
private static string Foo;

Now the thing that can trip you up is initialization. 

[ThreadStatic]
private static string Foo = "the foo string";

The ThreadStatic is initialized in the static constructor - which only executes once. So only the very first thread is assigned "the foo string" when the static constructor executes. When accessed in all subsequent threads, Foo is left at the uninitalized null value.

The best way to work around this is to use a property to access the Foo prop.

[ThreadStatic]
private static string _foo;

public static string Foo {
get {
if (_foo == null) {
_foo = "the foo string";
}
return _foo;
}
}

If you have something that can be legitimately set to null, you can always use a thread-static boolean "_fooInitialized".

[ThreadStatic]
private static string _foo;

[ThreadStatic]
private static bool _fooInitialized;

public static string Foo {
get {
if (!_fooInitialized) {
_fooInitialized = true;
_foo = "the foo string";
}
return _foo;
}
}

And if you have a lot of booleans, you may want to save space; believe it or not a boolean hogs precious bits (for extra credit try printing out Marshal.SizeOf(typeof(bool)). 

You save on space by lumping them all into one bitvector... 

        [ThreadStatic]
private static string _foo;

        [ThreadStatic]
private static string _goo;

        [ThreadStatic]
private static BitVector32 _state = new BitVector32();
 

        private const int stateFooInitialized = 0x0001;
private const int stateGooInitialized = 0x0002;
        // ... 0x0004, 0x0008 ...

        public static string Foo {
get {
if (!_state[stateFooInitialized]) {
_state[stateFooInitialized] = true;
_foo = "the foo string";
}
return _foo;
}
}

        public static string Goo {
get {
if (!_state[stateGooInitialized]) {
_state[stateGooInitialized] = true;
_goo = "the goo string";
}
return _goo;
}
}

Comments

  • Anonymous
    July 18, 2006
    Phil asks:

    "Your last post mentions using Marshal.SizeOf to get the byte size of a type that can be...

  • Anonymous
    July 18, 2006
    Instead of figuring out the correct bitmask for your state consts yourself, you can use the static BitVector32.CreateMask() method to do it for you. In this case you can write:

    static readonly int stateFooInitialized = BitVector32.CreateMask();
    static readonly int stateGooInitialized = BitVector32.CreateMask(stateFooInitialized );

    Note that in second call you should put the previous mask.

    Another great feature of BitVector32 I've learned recently is bit packing. You can read more here: http://adoguy.com/viewrant.aspx?id=2016

  • Anonymous
    July 18, 2006
    Man, I always learn the coolest things from your blog.

    It's interesting, I love annotations, but I find them much harder to discover than normal Framework classes. I stumble on new classes and methods all the time via Intellisense, but I don't know that I've ever found an annotation that way.

  • Anonymous
    July 19, 2006
    Kobush - CreateMask is really cool.  I believe theres also something like CreateSection if you want to map in more than one bit.  Some folks use this to stash away enumerations.

    The reason I didn't use CreateMask here is that I would have to store it off statically, which meant that instead of holding onto booleans, I was holding on to integers - so basically I wouldn't shave off any room.

    The const would get replaced by the compiler, so it would not take up room.

    I find CreateMask is only helpful when you're remembering non-static state.

  • Anonymous
    July 19, 2006
    Ok, in that case it makes perfect sense to use const instead. I've only used BitVector32 for classes that have lots of instances (e.g. ORM metadata), so sacrificing few statics to store masks wasn't big deal.

    Thanks for another great .NET tip!!!

  • Anonymous
    July 19, 2006
    The comment has been removed

  • Anonymous
    July 20, 2006
    Rico - the most common place for ThreadStatic to be used is for UI caches.  Quite a lot of the graphic objects cannot be accessed from another thread.  For example, holding onto a System.Drawing.Image or Graphics object statically is a big nono.

  • Anonymous
    July 24, 2006
    PingBack from http://technote.thedeveloperside.com/?p=58

  • Anonymous
    April 27, 2007
    A long time ago Patrick Cauldwell wrote up a technique for managing external files within unit tests

  • Anonymous
    January 21, 2008
    转载:http://blogs.msdn.com/jfoscoding/archive/2006/07/18/670497.aspx