配置线程静态变量 [英] Disposing thread static variable

查看:87
本文介绍了配置线程静态变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在静态类中有一个ThreadStatic成员.静态类用于多线程环境中.我想确保当线程返回到线程池(或重新使用)时,该成员被处置(或重新初始化),因此特定线程的任何后续使用都将获得该变量的新副本.该成员必须保持静态,因此实例成员将无济于事.

I have a ThreadStatic member in a static class. The static class is used in a multi threaded environment. I want to make sure that when a thread is returned to threadpool (or re-used), the member is disposed (or re-initialized), so any subsequent uses of the particular thread gets a fresh copy of the variable. The member has to stay static so an instance member will not really help.

我尝试使用ThreadLocalAsyncLocalCallContext,但是这些都没有帮助. (CallContext主要用于概念验证,它是.net标准应用程序,因此callcontext仍然无法正常工作.)

I have tried using ThreadLocal, AsyncLocal and CallContext but none of these really help. (CallContext is mostly for proof of concept, its a .net standard app so callcontext won't work anyways).

这只是我编写的用于重现具有ThreadStaticThreadLocalAsyncLocalCallContext进行测试的问题的示例代码.

This is just a sample code I wrote to recreate my problem having ThreadStatic, ThreadLocal, AsyncLocal and CallContext for testing.


    class Program
    {
        static void Main(string[] args)
        {
            var act = new List<Action<int>>()
            {
                v=> ThreadClass.Write(v),
                v=> ThreadClass.Write(v),
            };

            Parallel.ForEach(act, new ParallelOptions { MaxDegreeOfParallelism = 1 }, (val, _, index) => val((int)index));

            Console.WriteLine($"Main: ThreadId: {Thread.CurrentThread.ManagedThreadId} ThreadStatic = {ThreadClass.ThreadStatic} ThreadLocal = {ThreadClass.ThreadLocal.Value} AsyncLocal = {ThreadClass.AsyncLocal.Value} CallContext: {ThreadClass.CallContextData}");

            Console.ReadKey();
        }
    }

    public static class ThreadClass
    {
        static object _lock = new object();

        [ThreadStatic]
        public static string ThreadStatic;

        public static ThreadLocal<string> ThreadLocal = new ThreadLocal<string>(() => "default");


        public static readonly AsyncLocal<string> AsyncLocal = new AsyncLocal<string>();

        public static string CallContextData
        {
            get => CallContext.LogicalGetData("value") as string;
            set => CallContext.LogicalSetData("value", value);
        }

        static ThreadClass()
        {
            AsyncLocal.Value = "default";
        }


        public static void Write(int id)
        {
            lock (_lock)
            {
                Console.WriteLine($"{id} Init: ThreadId: {Thread.CurrentThread.ManagedThreadId} ThreadStatic = {ThreadStatic} ThreadLocal = {ThreadLocal.Value} AsyncLocal = {AsyncLocal.Value} CallContext: {ThreadClass.CallContextData}");

                ThreadStatic = $"Static({id})";
                ThreadLocal.Value = $"Local({id})";
                AsyncLocal.Value = $"Async({id})";
                CallContextData = $"Call({id})";

                Console.WriteLine($"{id} Chng: ThreadId: {Thread.CurrentThread.ManagedThreadId} ThreadStatic = {ThreadStatic} ThreadLocal = {ThreadLocal.Value} AsyncLocal = {AsyncLocal.Value} CallContext: {ThreadClass.CallContextData}");
            }

        }
    }

以上代码在单个线程中运行,因此可以重复使用该线程.

The above code is run in a single thread so the thread can be re-used.

0 Init: ThreadId: 1 ThreadStatic =  ThreadLocal = default AsyncLocal = default CallContext:
0 Chng: ThreadId: 1 ThreadStatic = Static(0) ThreadLocal = Local(0) AsyncLocal = Async(0) CallContext: Call(0)
--------------------
1 Init: ThreadId: 1 ThreadStatic = Static(0) ThreadLocal = Local(0) AsyncLocal = Async(0) CallContext: Call(0)
1 Chng: ThreadId: 1 ThreadStatic = Static(1) ThreadLocal = Local(1) AsyncLocal = Async(1) CallContext: Call(1)
--------------------
Main: ThreadId: 1 ThreadStatic = Static(1) ThreadLocal = Local(1) AsyncLocal =  CallContext:

但是,从输出中可以看到,当进行第二次调用并重用线程1时,它仍然具有由线程0设置的值.

However, as seen in the output, when the second call is made and thread 1 is reused, it still has the values set by thread 0.

在重新使用线程时,有什么方法可以将ThreadStatic变量重置为默认值或为null?

Is there any way to reset ThreadStatic variable to default value or null when thread is re used?

推荐答案

TL; DR

如果不希望变量在多线程应用程序中被多个线程重用,则没有理由将其设为静态.

If don't want a variable reused by multiple threads in a multi-threaded application, there's no reason to make it static.

如果我们不希望变量被同一线程重用,那么为什么我们故意使用[ThreadStatic]是有疑问的,因为这正是我们所允许的.

If we don't want a variable reused by the same thread, it's questionable why we would deliberately use [ThreadStatic], since that's what it allows us to do.

我正在关注此问题的ThreadStatic方面,因为它似乎是问题的焦点.

I'm focusing on the ThreadStatic aspect of this since it seems to be a focus of the question.

因此特定线程的任何后续使用都将获得该变量的新副本.

so any subsequent uses of the particular thread gets a fresh copy of the variable.

使用线程不需要它们自己的变量副本-使用变量的方法可以或可以不需要它们自己的变量副本.这听起来像是一团乱七八糟的话,但是线程本身并不需要任何变量的副本.它可能正在做与该静态类和此变量无关的事情.

Uses of the thread don't need their own copy of the variable - methods that use the variable may or may not need their own copy of the variable. That sounds like a hair-splitting thing to say, but a thread, by itself, doesn't need a copy of any variable. It could be doing things unrelated to this static class and this variable.

当我们使用该变量时,我们会在乎它是否是新副本".也就是说,当我们调用使用该变量的方法时.

It's when we use the variable that we care whether it is a "fresh copy." That is, when we invoke a method that uses the variable.

如果,当我们使用静态变量(在方法外部声明)时,我们想要的是确保在使用它之前先对其进行新实例化,并在完成该操作后将其释放,那么我们可以实现这一目标在使用它的方法中.我们可以实例化它,将其处置,如果需要,甚至可以将其设置为null.但是,这样做的结果是,通常可以消除在使用该方法的方法之外声明该变量的任何需要.

If, when we use the static variable (which is declared outside of a method), what we want is to ensure that it's newly instantiated before we use it and disposed when we're done with it, then we can accomplish that within the method that uses it. We can instantiate it, dispose it, even set it to null if we want to. What becomes apparent as we do this, however, is that it usually eliminates any need for the variable to be declared outside the method that uses it.

如果我们这样做:

public static class HasDisposableThreadStaticThing
{
    [ThreadStatic]
    public static DisposableThing Foo;

    public static void UseDisposableThing()
    {
        try
        {
            using (Foo = new DisposableThing())
            {
                Foo.DoSomething();
            }
        }
        finally
        {
            Foo = null;
        }
    }
}

我们已经完成了目标.

We've accomplished the goal.

在重用线程时,有什么方法可以将ThreadStatic变量重置为默认值或为null?

Is there any way to reset ThreadStatic variable to default value or null when thread is re used?

完成.每次同一线程进入方法(重新使用该线程")时,它为null.

Done. Every time the same thread enters the method ("the thread is re used") it's null.

但是如果这就是我们想要的,那为什么不这样做呢?

But if that's what we want, then why not just do this?

public static class HasDisposableThreadStaticThing
{
    public static void UseDisposableThing()
    {
        using (var foo = new DisposableThing())
        {
            foo.DoSomething();
        }
    }
}

结果完全相同.每个线程均以DisposableThing的新实例开头,因为执行该方法时,它将声明该变量并创建一个新实例.而不是将其设置为null,引用超出了范围.

The result is exactly the same. Every thread starts with a new instance of DisposableThing because when it executes the method it declares the variable and creates a new instance. Instead of setting it to null the reference goes out of scope.

两者之间的唯一区别是,在第一个示例中,DisposableThing在类之外公开公开.这意味着其他线程可以使用它而不是声明自己的变量,这很奇怪.由于他们还需要确保在使用它之前已实例化它,为什么他们不也像在第二个示例中那样只创建自己的实例?

The only difference between the two is that in the first example, DisposableThing is publicly exposed outside of the class. That means that other threads could use it instead of declaring their own variable, which is weird. Since they would also need to make sure it's instantiated before using it, why wouldn't they also just create their own instance as in the second example?

确保变量在静态方法中每次需要初始化和处置时,最简单,最普通的方法是在静态方法中本地声明该变量并创建一个新实例.然后,无论有多少线程同时调用它,它们都将使用单独的实例.

The easiest and most normal way to ensure that a variable is initialized and disposed every time it's needed in a static method is to declare that variable locally within the static method and create a new instance. Then regardless of how many threads call it concurrently they will each use a separate instance.

这篇关于配置线程静态变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆