简介
ThreadLocal 用于在多线程环境中创建线程局部变量,可以让每个线程独立地访问自己的变量副本,互不影响。
而 AsyncLocal 是 ThreadLocal 的异步版本,专门用于异步编程场景,在异步操作中它可以正确处理上下文切换。
ThreadLocal
ThreadLocal<string> ThreadName = new ThreadLocal<string>(() =>
{
return "Thread" + Thread.CurrentThread.ManagedThreadId;
});
// Action that prints out ThreadName for the current thread
Action action = () =>
{
// If ThreadName.IsValueCreated is true, it means that we are not the
// first action to run on this thread.
bool repeat = ThreadName.IsValueCreated;
Console.WriteLine("ThreadName = {0} {1}", ThreadName.Value, repeat ? "(repeat)" : "");
};
// 启动八个线程,在4核及一下的电脑上,一些线程名会重复
Parallel.Invoke(action, action, action, action, action, action, action, action);
//释放对象
ThreadName.Dispose();
private static ThreadLocal<WebContext> threadLocal = new ThreadLocal<WebContext>(() =>
{
var ctx = new WebContext();
Console.WriteLine($"创建WebContext");
return ctx;
});
Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
//模拟5个HTTP请求
for (var i = 0; i < 5; i++)
{
var index = i;
Task.Factory.StartNew(() =>
{
var ctx = threadLocal.Value;
ctx.Name = "请求" + index;
ctx.Id = index;
Console.WriteLine($"请求结束:{index} ctx.Name={ctx.Name} ctx.Id={ctx.Id}");
});
}
可以看到,不同的线程值是不同的
AsyncLocal
//父上下文
public static async Task Async()
{
asyncLocal.Value = new WebContext
{
Id = 0,
Name = "父"
};
Console.WriteLine("父:设定ctx:" + asyncLocal.Value);
await Async1();
Console.WriteLine("父:结束时ctx:" + asyncLocal.Value);
}
//子上下文
public static async Task Async1()
{
Console.WriteLine(" 子读取到ctx:" + asyncLocal.Value);
await Async1_1();
Console.WriteLine(" 经过孙处理后再读取ctx:" + asyncLocal.Value);
asyncLocal.Value = new WebContext
{
Name = "子",
Id = 1,
};
Console.WriteLine(" 子改动ctx为:" + asyncLocal.Value);
await Async1_1();
Console.WriteLine(" 经过孙处理后再读取ctx:" + asyncLocal.Value);
}
//孙上下文
public static async Task Async1_1()
{
Console.WriteLine(" 孙读取到ctx:" + asyncLocal.Value);
asyncLocal.Value = new WebContext
{
Name = "孙",
Id = 2,
};
Console.WriteLine(" 孙改动ctx为:" + asyncLocal.Value);
}
class WebContext
{
public string Name { get; set; }
public int Id { get; set; }
public override string ToString()
{
return $"Name={Name},Id={Id}";
}
}
AsyncLocal
使你能够更深入地访问操作的异步流中的相同状态。 AsyncLocal
值仅向下流动(更深入地进入异步调用堆栈),并且不会将更改向上传播到调用方。
//父上下文
public static async Task Async()
{
threadLocal.Value = new WebContext
{
Id = 0,
Name = "父"
};
Console.WriteLine("父:设定ctx:" + threadLocal.Value);
await Async1();
Console.WriteLine("父:结束时ctx:" + threadLocal.Value);
}
//子上下文
public static async Task Async1()
{
Console.WriteLine(" 子读取到ctx:" + threadLocal.Value);
await Async1_1();
Console.WriteLine(" 经过孙处理后再读取ctx:" + threadLocal.Value);
threadLocal.Value = new WebContext
{
Name = "子",
Id = 1,
};
Console.WriteLine(" 子改动ctx为:" + threadLocal.Value);
await Async1_1();
Console.WriteLine(" 经过孙处理后再读取ctx:" + threadLocal.Value);
}
//孙上下文
public static async Task Async1_1()
{
Console.WriteLine(" 孙读取到ctx:" + threadLocal.Value);
threadLocal.Value = new WebContext
{
Name = "孙",
Id = 2,
};
Console.WriteLine(" 孙改动ctx为:" + threadLocal.Value);
}
class WebContext
{
public string Name { get; set; }
public int Id { get; set; }
public override string ToString()
{
return $"Name={Name},Id={Id}";
}
}