【.NET深呼吸】存储基于本地线程的值

作者: 梁丘刘峰

更新时间:2022-03-26 13:10:43

4167 阅读

在特定情况,我们希望这样一个场景:

N个线程同时调用同一个类实例的同一个操作方法,并且同一个变量可以面向每一个线程存储独立的值。比如,某变量X,它对于线程A的值与对于线程B的值是相互独立的。线程A设置了X的值为3,那么只要代码是在线程A上执行的,那么变量X的值就是3;线程B设置X值为7,那么在线程B的代码中X的值就为7。

同样一个X变量,不同的线程访问它就会读写不同的值

 

有些时候,我们需要以上功能。只要把希望基于线程本地所使用的值的变量类型声明为ThreadLocal<T>类型即可,其中T表示该变量中要存储的值的数据类型。

下面定义一个类:

    public sealed class ThreadingWork
    {
        private Random m_rand = null;
        private ThreadLocal<int> m_localVal = default(ThreadLocal<int>);

        public ThreadingWork()
        {
            m_rand = new Random();
            m_localVal = new ThreadLocal<int>();
        }

        private void MakeRandom()
        {
            m_localVal.Value = m_rand.Next(0, 1000);
        }

        public void RunOnThreads()
        {
            // 为变量生成值
            MakeRandom();
            // 引发事件
            string str = $"在线程{Thread.CurrentThread.ManagedThreadId}上设置的值为:{m_localVal.Value}";
            ThreadingRuned?.Invoke(this, new RunThreadEventArgs(str));
        }

        public event EventHandler<RunThreadEventArgs> ThreadingRuned;
        
    }

    /// <summary>
    /// 自定义事件参数类
    /// </summary>
    public class RunThreadEventArgs : EventArgs
    {
        internal RunThreadEventArgs(string s)
        {
            Value = s;
        }

        public string Value { get; private set; }
    }

m_localVal变量是类的字段,它里面存放的是int类型的值。RunOnThreads方法会被不同的线程调用,线程执行方法后,会生成一个随机整数,并存到m_localVal变量中。接着引发ThreadingRuned事件,并将m_localVal变量中存放的值随着事件传递,以便被其他代码使用。

下面,我们测试一下。

            // 实例化对象
            ThreadingWork work = new ThreadingWork();
            // 附加事件处理
            work.ThreadingRuned += Work_ThreadingRuned;
            // 创建30个Task来干活
            for(int n = 0; n < 30; n++)
            {
                Thread t = new Thread(work.RunOnThreads);
                t.Start();
            }

上面代码创建了30个线程,并且线程所执行的都是同一个实例work上的RunOnThreads方法。

当代码运行后,见证奇迹的一刻到了。

 

从运行结果中可以发现,同一个实例方法被多个线程调用,但m_localVal变量可以存放来自各个线程的值,而每个线程所读取的都是基于当前线程的值,即每个线程读写的值都不同。

 

尤其是在实现多线程下载的案例中,可以使用同一个实例变量来记录源自不同线程的下载进度(假设每个线程开启一个下载任务)。

 

例子代码下载。

 

版权声明:本文著作权归作者【梁丘刘峰 】所有,不代表本网站立场。

侵权请联系:root_email@163.com

相关推荐