using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace MultiThreadingStudy.Core
{
    /// <summary>
    /// 线程本地存储 执行器
    /// </summary>
    public class ThreadLocalStorage
    {
        /// <summary>
        /// 不使用线程本地存储
        /// </summary>
        public static void Run()
        {
            var demo = new Computer();
            var thread_a = new Thread(new ThreadStart(demo.SumCount)) { Name = "thread_a" };
            var thread_b = new Thread(new ThreadStart(demo.SumCount)) { Name = "thread_b" };

            thread_a.Start();
            thread_b.Start();

            thread_a.Join();
            thread_b.Join();

            //调用线程:[主线程|内核线程] 共享字段的值。不加处理的话,可能小于多线程调用次数之和。
            Console.WriteLine("调用线程的共享变量值 = " + Computer.TotalCount);
        }

        /// <summary>
        /// 使用 ThreadStatic 特性
        /// </summary>

        public static void UseThreadStaticAttribute()
        {
            var demo = new ThreadStaticComputer();
            var thread_a = new Thread(new ThreadStart(demo.SumCount)) { Name = "thread_a" };
            var thread_b = new Thread(new ThreadStart(demo.SumCount)) { Name = "thread_b" };

            thread_a.Start();
            thread_b.Start();

            thread_a.Join();
            thread_b.Join();

            //调用线程:[主线程|内核线程] 共享字段的值。不加处理的话,可能小于多线程调用次数之和。
            Console.WriteLine("调用线程的共享变量值 = " + Computer.TotalCount);
        }

        /// <summary>
        /// 使用数据槽
        /// </summary>
        public static void UseDataSlot()
        {
            var demo = new DataSlotComputer();
            var thread_a = new Thread(new ThreadStart(demo.SumCount)) { Name = "thread_a" };
            var thread_b = new Thread(new ThreadStart(demo.SumCount)) { Name = "thread_b" };

            thread_a.Start();
            thread_b.Start();

            thread_a.Join();
            thread_b.Join();

            //调用线程:[主线程|内核线程] 共享字段的值。不加处理的话,可能小于多线程调用次数之和。
            Console.WriteLine("调用线程的共享变量值 = " + Computer.TotalCount);
        }

        /// <summary>
        /// 使用 ThreadLocal 泛型类
        /// </summary>
        public static void UseThreadLocalClass()
        {
            var demo = new ThreadLocalComputer();
            var thread_a = new Thread(new ThreadStart(demo.SumCount)) { Name = "thread_a" };
            var thread_b = new Thread(new ThreadStart(demo.SumCount)) { Name = "thread_b" };

            thread_a.Start();
            thread_b.Start();

            thread_a.Join();
            thread_b.Join();

            //调用线程:[主线程|内核线程] 共享字段的值。不加处理的话,可能小于多线程调用次数之和。
            Console.WriteLine("调用线程的共享变量值 = " + Computer.TotalCount);
        }
    }

    /// <summary>
    /// 不使用本地存储
    /// </summary>
    /// <remarks>
    ///  由于 ++ -- += -= 等对共享变量的操作不是原子性的,多线程下是不安全的。
    /// </remarks>
    public class Computer
    {
        /// <summary>
        /// 总计算次数
        /// </summary>
        //[ThreadStatic]
        public static int TotalCount = 0;

        /// <summary>
        /// 循环次数
        /// </summary>
        public readonly int LoopNumber = 1000;

        /// <summary>
        /// 累加计数
        /// </summary>
        public void SumCount()
        {
            for (int i = 1; i <= LoopNumber; i++)
            {
                //因为 += 是和等于的两步操作,所以不是线程安全的。
                TotalCount += 1;

                //如果使用原子操作,则是线程安全的
                //Interlocked.Increment(ref TotalCount);

                //加休眠是为了让并发效果明显,不加休眠可以增加循环次数
                Thread.Sleep(1);
            }

            Console.WriteLine($"线程[{Thread.CurrentThread.ManagedThreadId.ToString("000")}]执行后,共享变量 {nameof(TotalCount)} = {TotalCount}");            
        }
    }

    /// <summary>
    /// 使用本地存储:ThreadStatic 特性
    /// </summary>
    /// <remarks>
    ///  特点:只能用于静态变量上,不能用于实例上(语法不报错,实际不起作用)
    /// </remarks>
    public class ThreadStaticComputer
    {
        /// <summary>
        /// 总计算次数
        /// </summary>
        [ThreadStatic]
        public static int TotalCount = 0;

        /// <summary>
        /// 循环次数
        /// </summary>
        public readonly int LoopNumber = 100;

        /// <summary>
        /// 累加计数
        /// </summary>
        public void SumCount()
        {
            for (int i = 1; i <= LoopNumber; i++)
            {
                TotalCount += 1;

                Thread.Sleep(0);
            }
            Console.WriteLine($"线程[{Thread.CurrentThread.ManagedThreadId.ToString("000")}]执行后,共享变量 {nameof(TotalCount)} = {TotalCount}");
        }
    }

    /// <summary>
    /// 使用本地存储:数据槽 
    /// </summary>
    /// <remarks>
    ///  特点:可以用于静态也可用于实例
    /// </remarks>
    public class DataSlotComputer
    {
        /// <summary>
        /// 总计算次数
        /// </summary>
        public static int TotalCount = 0;

        /// <summary>
        /// 循环次数
        /// </summary>
        public readonly int LoopNumber = 100;

        /// <summary>
        /// 数据槽
        /// </summary>
        public LocalDataStoreSlot storeSlot = Thread.AllocateDataSlot(); 

        /// <summary>
        /// 累加计数值
        /// </summary>
        public void SumCount()
        {
            for (int i = 1; i <= LoopNumber; i++)
            {
                Thread.SetData(storeSlot, i);

                //加休眠是为了让并发效果明显,不加休眠可以增加循环次数
                Thread.Sleep(0);
            }

            //严格讲,这里也不是线程安全的。少量线程不安全的概率很小.可以使用原子操作实现。
            TotalCount += (int)Thread.GetData(storeSlot);
            //保证线程安全的话,可用原子操作
            //Interlocked.Add(ref TotalCount, (int)Thread.GetData(storeSlot));

            Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId.ToString("000")}执行后,共享变量{nameof(TotalCount)} = {TotalCount}");
        }
    }

    /// <summary>
    ///  使用本地存储:ThreadLocal泛型类
    /// </summary>
    /// <remarks>
    ///   特点:1、对静态和实例字段都提供了线程本地存储支持,并允许指定默认值
    ///         2、ThreadLocal的值是延迟计算的:每线程第一次调用时计算实际的值
    /// </remarks>
    public class ThreadLocalComputer
    {
        /// <summary>
        /// 总计算次数
        /// </summary>
        public static int  TotalCount = 0;

        /// <summary>
        /// 循环次数
        /// </summary>
        public readonly int LoopNumber = 100;

        /// <summary>
        /// 线程本地存储数据
        /// </summary>
        public static ThreadLocal<int> threadTotalCount = new ThreadLocal<int>();
        /// <summary>
        /// 累加计数值
        /// </summary>
        public void SumCount()
        {
            for (int i = 1; i <= LoopNumber; i++)
            {

                threadTotalCount.Value += 1;

                //加休眠是为了让并发效果明显,不加休眠可以增加循环次数
                Thread.Sleep(0);
            }

            TotalCount += threadTotalCount.Value;
            //保证线程安全的话,可用原子操作
            //Interlocked.Add(ref TotalCount, threadTotalCount.Value);

            Console.WriteLine($"线程{Thread.CurrentThread.ManagedThreadId.ToString("000")}执行后,共享变量{nameof(TotalCount)} = {TotalCount}");
        }
    }
}