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

时间:2022-06-02 09:48:58

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

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变量可以存放来自各个线程的值,而每个线程所读取的都是基于当前线程的值,即每个线程读写的值都不同。

 

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

 

例子代码下载