转载

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

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

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方法。

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

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

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

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

例子代码下载。

正文到此结束
Loading...