(精华)2020年12月23日 .NET Core 多线程底层详解(线程本地存储)

101 阅读1分钟

一般来说线程都是有自己上下文,变量是不共享的,这就需要线程本地存储

public static class TlsSample
{
    [ThreadStatic]
    public static int a;
    [ThreadStatic]
    public static int b;

    public static void Thread1()
    {
        a = 1;
        b = 2;
        Console.WriteLine($"a={a} [From Thread1]");
        Console.WriteLine($"b={b} [From Thread1]");
    }

    public static void Thread2()
    {
        a = 10;
        b = 20;
        Console.WriteLine($"a={a} [From Thread2]");
        Console.WriteLine($"b={b} [From Thread2]");
    }

    public static void Run()
    {
        var thread1 = new Thread(Thread1);
        var thread2 = new Thread(Thread2);

        thread1.Start();
        thread2.Start();
    }
}
public class ThreadLocalSample
{
    public readonly ThreadLocal<int> a = new ThreadLocal<int>();
    public readonly ThreadLocal<int> b = new ThreadLocal<int>();

    public void Thread1()
    {
        a.Value = 1;
        b.Value = 2;
        Console.WriteLine($"a={a} [From Thread1]");
        Console.WriteLine($"b={b} [From Thread1]");
    }

    public void Thread2()
    {
        a.Value = 10;
        b.Value = 20;
        Console.WriteLine($"a={a} [From Thread2]");
        Console.WriteLine($"b={b} [From Thread2]");
    }

    public void Run()
    {
        var thread1 = new Thread(Thread1);
        var thread2 = new Thread(Thread2);

        thread1.Start();
        thread2.Start();
    }
}

异步本地变量AsyncLocal(执行上下文)

public static class Sample07
{
    private static readonly ThreadLocal<int> ThreadLocal = new ThreadLocal<int>();
    private static readonly AsyncLocal<int> AsyncLocal = new AsyncLocal<int>();

    public static async Task Run()
    {
        ThreadLocal.Value = 10;
        AsyncLocal.Value = 10;

        Console.WriteLine($"Tid={Thread.CurrentThread.ManagedThreadId};" +
                          $"ThreadLocal={ThreadLocal.Value};" +
                          $"AsyncLocal={AsyncLocal.Value}");

        await Task.Delay(1000);

        Console.WriteLine($"Tid={Thread.CurrentThread.ManagedThreadId};" +
                          $"ThreadLocal={ThreadLocal.Value};" +
                          $"AsyncLocal={AsyncLocal.Value}");

    }
}

不变性值类型子任务异步变量修改不会变,引用会

public static class Sample08
{
    private static readonly AsyncLocal<int> AsyncLocal = new AsyncLocal<int>();

    public static async Task ParentTask()
    {
        AsyncLocal.Value = 111;
        Console.WriteLine($"ParentTask Begin:AsyncLocal={AsyncLocal.Value}");
        await ChildTask();
        Console.WriteLine($"ParentTask End:AsyncLocal={AsyncLocal.Value}");
    }

    public static async Task ChildTask()
    {
        Console.WriteLine($"ChildTask Begin:AsyncLocal={AsyncLocal.Value}");
        AsyncLocal.Value = 222;
        await Task.Delay(1000);
        Console.WriteLine($"ChildTask End:AsyncLocal={AsyncLocal.Value}");
    }
}

禁止捕捉执行上下文,则无法传递到其他线程

public static class Sample11
{
    private static readonly AsyncLocal<int> AsyncLocal = new AsyncLocal<int>();

    public static async Task ParentTask()
    {
        AsyncLocal.Value = 111;
        Console.WriteLine($"ParentTask Begin:AsyncLocal={AsyncLocal.Value}");

        // 禁止捕捉执行上下文
        var control = ExecutionContext.SuppressFlow();

        Task.Delay(100).ContinueWith(task =>
        {
            Console.WriteLine($"SuppressFlow:AsyncLocal={AsyncLocal.Value}");

        });

        // 恢复捕捉上下文
        control.Undo();

        // 捕捉到的执行上下文为null
        await Task.Delay(100);
        Console.WriteLine($"Undo:AsyncLocal={AsyncLocal.Value}");
    }
}