多线程

SunnyFan大约 19 分钟约 5744 字

多线程

多线程特点:线程是计算机资源

Thread

来自于System.Threading的一个密封类,他是在.NETopen in new window Framorework1.0时代出现的,在c#中用来操作计算机资源线程的一个帮助类库

 {
    {
        ParameterizedThreadStart parameterizedThreadStart = new ParameterizedThreadStart((oInstacnce) =>
        {
            Debug.WriteLine($"****************ParameterizedThreadStart    {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString(" HH:mm:ss.fff")}***************");
        });
        Thread thread = new Thread(parameterizedThreadStart);
        thread.Start();//开启了一个新的线程;
    }
    {
        ThreadStart threadStart = new ThreadStart(() =>
        {
            this.DoSomething("Richard");
        });
        Thread thread = new Thread(threadStart);
        thread.Start();//开启了一个新的线程;
    }
}
{
    Thread thread = new Thread(() =>
    {
        Thread.Sleep(5000);
        this.DoSomething("Richard");
    });
    thread.Suspend(); //1.表示线程暂停;---并不能马上停止下来;
    thread.Resume();  //2.线程恢复执行
    thread.Abort();     //3.线程停止--子线程对外抛出了一个异常; 线程是无法从外部去终止的;
    Thread.ResetAbort();//4.停止的线程继续去执行
    //1.线程等待的:ThreadState有多种状态;如果线程停止了,状态会修改;
    while (thread.ThreadState != System.Threading.ThreadState.Stopped) //如果线程没有停止;
    {
        Thread.Sleep(500); //当前休息500ms  不消耗计算机资源的
    }
    //2.自己支持的线程等待: 
    thread.Join();//等待线程中的内容执行完毕;继续往后; 
    thread.Join(500);//等待500ms,过时不候;
    thread.Join(TimeSpan.FromMilliseconds(500));//等待500ms,过时不候;
    thread.IsBackground = true;// 是后台线程:程序强制关掉,线程也就随之消失了; 
    thread.IsBackground = false; //是前台线程:程序强制关掉,线程会等待,内部的行为执行完毕,然后才结束;
    thread.Start();
}
{
    Thread thread = new Thread(() =>
    {
        this.DoSomething("Richard");
    });
    // 线程的优先级最高
    thread.Priority = ThreadPriority.Highest;

    Thread thread1 = new Thread(() =>
    {
        this.DoSomething("Richard");
    });
    // 线程的优先级最低
    thread1.Priority = ThreadPriority.Lowest;
    thread.Start();
    thread1.Start();//线程开启后,根据优先级,来执行;---设置优先级只是提高了他被有限执行的概率; 
} 
//1.有两个动作:  两个委托;  2.必须是多线程执行    3.要求两个委托顺序; 
{
    ThreadStart threadStart = new ThreadStart(() =>
    {
        this.DoSomething("Richard");
    });
    Action action = () =>
    {
        this.DoSomething("Richard01");
    };
    //Thread thread = new Thread(threadStart);
    //thread.Start();

    //Thread thread1 = new Thread(() =>
    //{
    //    action.Invoke();
    //});
    //thread1.Start(); 
    this.CallBackThread(threadStart, action);//多线程执行两个行为,让两个行为具有顺序之分;
}

/// <summary>
/// 通过Thread扩展封装做到了,多线程执行两个行为,让两个行为具有顺序之分;
/// </summary>
/// <param name="threadStart"></param>
/// <param name="action"></param>
private void CallBackThread(ThreadStart threadStart, Action action)
{
    Thread thread = new Thread(() =>
    {
        threadStart.Invoke();
        action.Invoke();
    });
    thread.Start();
}

//如果有一个带返回值的委托,需要让你多多线程来执行:
{
    //希望这个委托能够做到异步执行;
    //既想要得到结果,有想多线程执行---做不到;
    //
    Func<int> func = () =>
    {
        Thread.Sleep(5000);
        return DateTime.Now.Year;
    };
    //func.BeginInvoke();///.NET Core 中已经不支持了;
    //int iResult = this.CallBackFunc<int>(func); 
    Func<int> func1 = this.CallBackFunc<int>(func);//这一步不会阻塞界面;
    Debug.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    Debug.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    Debug.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    Debug.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    Debug.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    Debug.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    Debug.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&");
    int iResult = func1.Invoke(); //在这里可以得到结果; 
    // 
}

/// <summary>
/// 多线程执行带有返回值的委托;
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
private Func<T> CallBackFunc<T>(Func<T> func)
{
    T t = default(T);
    ThreadStart threadStart = new ThreadStart(() =>
    {
        t = func.Invoke();
    });
    Thread thread = new Thread(threadStart);
    thread.Start();
    return new Func<T>(() =>
    {
        thread.Join();//等待thread执行完成;
        return t;
    });
}

ThreadPool

.NET Framework2.0时代:出现了一个线程池ThreadPool:是一种池化思想,相当于是在池子中,有线程存在;如果需要使用线程;就可以直接到线程池中去获取直接使用,如果使用完毕,在自动的回放到线程池中去;

{
    WaitCallback waitCallback = o =>
    {
        this.DoSomething("Richardd");
    };
    ThreadPool.QueueUserWorkItem(waitCallback, "1231345");
}
{
    ManualResetEvent resetEvent = new ManualResetEvent(false); //默认为False
    ThreadPool.QueueUserWorkItem(o =>
    {
        this.DoSomething(o.ToString());
        resetEvent.Set();
    }, "Richard");

    {
        //在这里做一些其他的的事情;
        Debug.WriteLine("123456");
        Debug.WriteLine("123456");
        Debug.WriteLine("123456");
    }
    resetEvent.WaitOne();//主线程等待; 等到resetEvent中的一个方法resetEvent.Set();执行: resetEvent.Set()执行了,主线程等待的这个WaitOne()就继续往后执行; 
}

提示

不建议大家去这样控制线程数量;因为后面要讲解的Task Prallel async await 他们的线程都是来自于线程池的;
如果通过SetMinThreads/SetMaxThreads来设置线程的数量;这个数量访问是在当前进程是全局的;

{
    {
        int workerThreads = 4;
        int completionPortThreads = 4;
        ThreadPool.SetMinThreads(workerThreads, completionPortThreads);
    }
    {
        int workerThreads = 8;
        int completionPortThreads = 8;
        ThreadPool.SetMaxThreads(workerThreads, completionPortThreads);
    }
    {
        ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads);
        Debug.WriteLine($"当前进程最小的工作线程数量:{workerThreads}");
        Debug.WriteLine($"当前进程最小的IO线程数量:{completionPortThreads}");
    }
    {
        ThreadPool.GetMaxThreads(out int workerThreads, out int completionPortThreads);
        Debug.WriteLine($"当前进程最大的工作线程数量:{workerThreads}");
        Debug.WriteLine($"当前进程最大的IO线程数量:{completionPortThreads}");
    } 
}

Async

{
    Debug.WriteLine($"****************btnSync_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    for (int i = 0; i < 5; i++)
    {
        DoSomething("kulala");
    }
    Debug.WriteLine($"****************btnSync_Click End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

private void DoSomething(string name)
{
    Debug.WriteLine($"****************DoSomething Start  {name}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    long lResult = 0;
    for (int i = 0; i < 1000_000_000; i++)
    {
        lResult += i;
    }
    Thread.Sleep(2000);
    Debug.WriteLine($"****************DoSomething   End  {name}  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}

异步方法:

{
    Debug.WriteLine($"****************btnAsyncAdvanced_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");

    #region 委托异步调用
    //在ASP.NET Core的年代:BeginInvoke不再支持了;如果想要看看这个,可以参考第14期;
    //Action<string> action = this.DoSomething;
    //AsyncCallback asyncCallback = null;
    //action.BeginInvoke(" Richard ", asyncCallback, "Object");
    #endregion

    //Task开启了一个线程 
    for (int i = 0; i < 5; i++)
    {
        Task.Run(() =>
        {
            DoSomething("kulala");
        }); 
    }
             
    Debug.WriteLine($"****************btnAsyncAdvanced_Click End   {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

Task

{
    {
        Task task = new Task(new Action(() =>
        {
            Debug.WriteLine("欢迎大家来到.NET进阶学习");
            this.DoSomething("Richard");
        }));
        task.Start();//线程开启了
    }
    {
        Task.Run(() =>
        {
            this.DoSomething("Richard");
        });
        Task.Run<int>(() =>
        {
            return DateTime.Now.Year;
        });
    }
    {
        TaskFactory factory = Task.Factory;
        factory.StartNew(() =>
        {
            this.DoSomething("一意");
        });
    }
    {
        TaskFactory factory = new TaskFactory();
        Task<int> tTask = factory.StartNew<int>(() => DateTime.Now.Year);
    }
}    
{
    Task task = new Task(() =>
    {
        Debug.WriteLine($"****************task开始了: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
        Task task1 = new Task(() =>
        {
            Debug.WriteLine($"****************task1: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
            Thread.Sleep(1000);
            Debug.WriteLine("我是task1线程");
        });

        Task task2 = new Task(() =>
        {
            Debug.WriteLine($"****************task2: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
            Thread.Sleep(1000);
            Debug.WriteLine("我是task2线程");
        });
        task1.Start();
        task2.Start();

        //task1.Wait();
        //task2.Wait();

        Debug.WriteLine($"****************task: {Thread.CurrentThread.ManagedThreadId.ToString("00")} ***************");
    });
    task.Start();//线程启动了
    task.Wait();   //单个线程的等待;等待task 内部的内容执行完毕,这里会卡顿界面;是主线程等待;

    // task.Wait();//等待执行完成:
    task.Wait(TimeSpan.FromMilliseconds(1100));
    task.Wait(1000);//限时等待;过时不候

    // Thread thread = null;

    //thread.Abort();//线程停止;
    //thread.Start();
    //thread.IsBackground = true;//后台线程:进程结束,线程随之消失
    //thread.IsBackground = false;//前台线程:进程结束,线程把内部需要执行的动作执行完毕,然后消失

}
//TaskCreationOptions.PreferFairness 相对来说比较公平执行的:如果是先申请的线程,就会优先执行;
{
    Task task1 = new Task(() =>
    {

    }, TaskCreationOptions.PreferFairness);

    Task task2 = new Task(() =>
    {

    }, TaskCreationOptions.PreferFairness);
}
{
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        Thread.Sleep(3000);//主线程执行到这儿会卡顿等待;等待3000ms后继续往后;   
        stopwatch.Stop();
        Debug.WriteLine($"Thread.Sleep(3000):{stopwatch.ElapsedMilliseconds}");
    }
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        //回调:Delay一般和ContinueWith配合使用;
        //不卡顿界面:3000ms以后,去执行一段业务逻辑:执行的动作就是ContinueWith内部的委托;ContinueWith内部的执行有可能是一个全新的线程去执行,也有可能是主线程去执行; 
        Task.Delay(3000).ContinueWith(t =>
        {
            Debug.WriteLine($"****************Delay || 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
            stopwatch.Stop();
            Debug.WriteLine($"Task.Delay(3000):{stopwatch.ElapsedMilliseconds}");
        });//一般情况下,这个Delay 是等待3000ms继续做点什么---或者说延迟3000ms后做点什么; 
    }
}

    提示

    有一个列表数据:有可能是来自于第三方接口,有可能是来自于缓存,也有可能是来自于数据库,查询的时候,我们不确定;
    常规玩法: 先找缓存,如果有数据,直接返回了,如果缓存没有--可能要去查询数据库,如果数据库有数据,直接返回,如果没有---查询第三方接口;
    多线程:同时开启三个线程,同时去查询三个地方的数据,只要是有一个地方的数据查询到了,只要是有一个地方的数据找到了,其他的两个线程我们就不管了;直接把找到的数据返回给前端就可以了;

    提示

    如果有一个首页,首页中包含了很多功能:考勤信息,年度top10,季度top10,公告,通知;
    在主页中:要加载主页-这些信息都需要查询出来: 而且这些信息都是来自于不同的地方,有的来自于接口,有的来自于缓存有的都来于数据库: 有几个块信息,就开启几个线程,同时分别去各自的地方进行查询各自的数据;可以在后台等待所有的子线程执行结束;拿到结果、组件一个大的复杂实体对象,传递给视图,通过视图做绑定;--让查询并发执行: 但是这里如果是使用C#开发,不做前后端分离的话,可以考虑这种,可以提高性能;
    现在更多的时候,可能是使用异步Ajax来完成---用户体验会更好; 资源会有所浪费,但是性能可以提高;

    {
        {
            List<Task> taskList = new List<Task>();
            TaskFactory factory = new TaskFactory();
            taskList.Add(factory.StartNew(obj => Coding("嘟嘟", "权限的数据库设计"), "嘟嘟"));
            taskList.Add(factory.StartNew(obj => Coding("年轻人不讲武德", "WeChat接口对接"), "年轻人不讲武德"));
            taskList.Add(factory.StartNew(obj => Coding("闻", "脚手架框架搭建"), "闻"));
            taskList.Add(factory.StartNew(obj => Coding("超越", "ABPVNetxt框架搭建"), "超越"));
            taskList.Add(factory.StartNew(obj => Coding("角印", "编写Webapi"), "角印"));
    
            {
            //相当于是一个回调;主线程执行的时候,这里是不卡段界面的;当taskList集合中的某一个线程执行结束语了;就触发后面委托中的动作;---不卡顿界面--用户体验就更好
            factory.ContinueWhenAny(taskList.ToArray(), ts =>
            {
                Debug.WriteLine($"{ts.AsyncState}同学开发完毕,Richard老师开始准备环境。。。");
                    });
    
                    //
            }
            {
                factory.ContinueWhenAll(taskList.ToArray(), ts =>
                {
                    Debug.WriteLine($"所有人开发完毕,我们一起庆祝一下,一起吃个饭!");
                });
            }
    
            {
                //Task.WaitAny--等待几个线程中的某一个线程执行结束;---主线程会等待-- - 会卡顿界面;一直到某一个线程执行结束后,才往后执行;--体验不好
                Task.WaitAny(taskList.ToArray());//主线程等待,其中某一个线程执行结束,然后继续往后执行;
                Debug.WriteLine("xxxx同学开发完毕,Richard老师开始准备环境。。。");
            }
            {
                //Task.WaitAll--等待几个线程都执行结束;---主线程会等待-- 一直到所有的线程执行结束,才往后执行;- 会卡顿界面;--体验不好
                Task.WaitAny(taskList.ToArray());//主线程等待,其中某一个线程执行结束,然后继续往后执行;
                Task.WaitAll(taskList.ToArray());
                Debug.WriteLine($"所有人开发完毕,我们一起庆祝一下,一起吃个饭!");
            }
    
            List<Action> actions = new List<Action>();
            actions.Add(() => { });
            actions.Add(() => { });
            actions.Add(() => { });
            actions.Add(() => { });
            actions.Add(() => { });
            actions.Add(() => { });
            actions.Add(() => { });
    
            ParallelOptions options = new ParallelOptions();
            options.MaxDegreeOfParallelism = 2; //下面在开启线程执行委托的时候,最多开启2个线程;
    
            Parallel.Invoke(options,actions.ToArray()); //会开启四个线程分别去执行;
            //如果想要限制线程数量
            }
            Debug.WriteLine($"****************btnTask_Click End || 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    }
    
{
    Debug.WriteLine($"****************TsakAdvanced_Click start || 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    //一、多线程中的异常情况
    //1.你们在开发中,如果要捕捉异常---普通方法--try-catch
    //2.多线程中,如果发生异常,使用try-catch包裹,捕捉不到异常
    //3.异常去哪儿了呢?  异常肯定是被吞掉了
    //4.多线程中,如果要捕捉异常---怎么做?
    //5.多线程中,如果要捕捉异常,需要设置主线程等待子线程执行结束;可以捕捉到异常
    //6.多线程内部发生异常后,抛出的异常类型是:system.AggregateException(多线程的异常类型)
    //7.如果(try-catch--catch)给定多个异常类型类匹配,多线程中,异常之后,有限配具体,如果没有具体,再匹配抽象
    {
        try
        {
            {
                //Task task = Task.Run(() =>
                // {
                //     int i = 0;
                //     int j = 10;
                //     int k = j / i; //尝试除以0  会异常
                // });

                //List<Task> tasks = new List<Task>();
                //tasks.Add(task);
                //Task.WaitAll(tasks.ToArray()); 
            }
            List<Task> tasklist = new List<Task>();
            for (int i = 0; i < 20; i++)
            {
                string keywork = $"TsakAdvanced_Click_{i}";
                tasklist.Add(Task.Run(() =>
               {
                   Thread.Sleep(new Random().Next(50, 100));
                   if (keywork == "TsakAdvanced_Click_6")
                   {
                       throw new Exception("TsakAdvanced_Click_6");
                   }
                   else if (keywork == "TsakAdvanced_Click_9")
                   {
                       throw new Exception("TsakAdvanced_Click_9");
                   }
                   else if (keywork == "TsakAdvanced_Click_12")
                   {
                       throw new Exception("TsakAdvanced_Click_12");
                   }
               }));
            }
            Task.WaitAll(tasklist.ToArray());
        }
        catch (AggregateException aex) //具体
        {
            Debug.WriteLine(aex.Message);
            foreach (var exception in aex.InnerExceptions)
            {
                Console.WriteLine(exception.Message);
            }
        }
        catch (Exception ex) //父类(一切异常的父类) 抽象
        {
            Debug.WriteLine(ex.Message);
        }
    }
    //故事、如果三个线程去执行一个业务逻辑体(查询---线程1--执行修改数据库;线程2---执行修改缓存,线程三--执行一个调用接口去修改另外一个服务器上的数据)
    //以上这个逻辑体执行过程中,如果有一个线程是异常了;在这个逻辑体中,其实就是一个残缺的--表示整个逻辑就有问题! 
    //多线程执行的时候,有时候,必须是多个线程都执行成功,才算成功,只要有一个线程异常了,就表示都异常----既然都是异常了,就应该让线程取消;已经异常了,就没哟必须继续往后了
    //二、线程取消;  线程是无法从外部取消的(除非关闭进程),只能自己取消自己---只有上帝才能打败上帝;
    //1.线程如何自己取消自己呢?----线程自己取消---其实就是向外抛出一个异常了; 
    //关于Thread/ThreadPool  看过录播的刷个1   否则刷个2 
    //2.如何取消线程呢?---具体场景中,应该怎么做呢?
    //3.标准的线程取消
    // a) 实例化一个 CancellationTokenSource 
    // b)  包含了一个IsCancellationRequested 属性,属性值默认为false
    // c)  包含了一个Cancel方法--Cancel 方法如果被执行--CancellationTokenSource 内部的属性值马上---false--true; 且只能从  false--true  不能由true --false
    //cts线程取消--看哪个线程跑的慢,如果有一个线程发生了异常了,跑的慢的线程,就会被取消掉(异常掉),如果跑的都比较快的线程--当还没有线程发生异常的时候,这个比较快的线程已经结束了,那么这个结束的线程是无法被取消的;
    //4. 当有一个线程发生异常的时候,其他线程就会有三种情况 1.已经结束的线程--不管(管不住); 2.已经正常开始,我可以让你取消,让这线程在结束的时候,抛出异常(取消了);3.还有部分的线程根本都还没有开启:只要有异常的线程,就应该让没有开启的线程不再开启了
    {
        //Thread thread = null;
        //thread.Abort(); //取消线程
    }
    {
        //Task task = Task.Run(() =>
        // {
        //     int i = 0;
        //     int j = 10;
        //     int k = j / i; //尝试除以0  会异常
        // });
        //List<Task> tasks = new List<Task>();
        //tasks.Add(task);
        //Task.WaitAll(tasks.ToArray()); 
    }
    try
    {
        List<Task> tasks = new List<Task>();
        CancellationTokenSource cts = new CancellationTokenSource();
        for (int i = 0; i < 100; i++)
        {
            string keywork = $"TsakAdvanced_Click_{i}";
            tasks.Add(Task.Run(() =>
            {
                Thread.Sleep(new Random().Next(10, 300));
                //if (cts.IsCancellationRequested == false)
                //{
                //    Debug.WriteLine($" 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 正常开始了");
                //}
                //else
                //{
                //    Debug.WriteLine($" 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 没有正常开始了");
                //    //throw new Exception($"{Thread.CurrentThread.ManagedThreadId.ToString("00") }取消了。。。");
                //}
                cts.Token.ThrowIfCancellationRequested();
                try
                {
                    if (keywork == "TsakAdvanced_Click_6")
                    {
                        throw new Exception("TsakAdvanced_Click_6");
                    }
                }
                catch (Exception)
                {
                    cts.Cancel();
                }
                cts.Token.ThrowIfCancellationRequested();
                //if (cts.IsCancellationRequested == false)
                //{
                //    Debug.WriteLine($" 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 正常结束了。。。");
                //}
                //else
                //{ 

                //    Debug.WriteLine($" 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 没有正常结束了。。。");
                //    //throw new Exception($"{Thread.CurrentThread.ManagedThreadId.ToString("00") }取消了。。。" );
                //}
            }, cts.Token)); //有线程发生异常后,还没有开启的线程就不再开启了
        }
        Task.WaitAll(tasks.ToArray());
    }
    catch (AggregateException aex)
    {
        foreach (var exception in aex.InnerExceptions)
        {
            Debug.WriteLine(exception.Message);
        } 
    } 
    Debug.WriteLine($"****************TsakAdvanced_Click End || 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

Parallel

{

    //如果我需要一次开启多个线程来执行一部分动作呢?

    Debug.WriteLine($"****************btnParallel_Click Start || 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
    {
        {
            //List<Action> acitonList = new List<Action>();
            //acitonList.Add(() => { });
            //acitonList.Add(() => { });
            //acitonList.Add(() => { });
            //Parallel.Invoke(acitonList.ToArray());//也是可以开启线程,可以一次放入很多个委托去执行;
        }
        //一、Parallel
        //1.可以传入多个委托
        //2.多个委托中的内容是会开启线程来执行---执行这里的线程有可能是新的线程,也有可能是主线程参与计算的
        //3.会阻塞主线程---相当于是主线程等待子线程执行结束

        {
            ParallelOptions options = new ParallelOptions();
            options.MaxDegreeOfParallelism = 3;
            Parallel.Invoke(options,
                () =>
                {
                    Debug.WriteLine($"线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                    this.DoSomething("this is Action 01");
                },
                () =>
                {
                    Debug.WriteLine($"线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                    this.DoSomething("this is Action 02");
                },
                () =>
                {
                    Debug.WriteLine($"线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                    this.DoSomething("this is Action 03");
                });
        }
        //二、能不能然他不阻塞界面?包一个Task
        {
            //Task.Run(() =>
            //{ 
            //    Parallel.Invoke(
            //  () =>
            //  {
            //      Debug.WriteLine($"线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            //      this.DoSomething("this is Action 01");
            //  },
            //  () =>
            //  {
            //      Debug.WriteLine($"线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            //      this.DoSomething("this is Action 02");
            //  },
            //  () =>
            //  {
            //      Debug.WriteLine($"线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            //      this.DoSomething("this is Action 03");
            //  });
            //});
        }
        //三、还不是开启了多个线程去执行委托?有啥作用:--Parallel是基于Task一个封装;
        //1.既然是开启多个线程--Parallel.For
        //2.如果有一大堆的任务要执行;100个任务---需要多线来执行:100个任务---开启100个线程?--不好
        //2.控制线程的数量---Parallel就是可以控制线程的数量,不至于出现线程泛滥的情况
        //

        {
            ParallelOptions options = new ParallelOptions();
            options.MaxDegreeOfParallelism = 3;
            Parallel.For(0, 100, options, index =>
             {
                 Debug.WriteLine($"index:{ index}  线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                 this.DoSomething("this is Action 03");
             });
        }
        {
            List<int> intlist = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };  
            //100个
            //100个任务: 如果我使用Parallel来开启线程计算;线程会有100个线程来执行--线程数量会过多;
            //1.最好能够控制线程数量   100个任务;  限定开启5个线程;控制反正一共就只有五个线程参与计算
            //2.相当于5个线程去平摊这100个任务---既开启了多个线程来执行任务---提高性能---且没有过多的开启线程(过多的开启线程会过多的损耗计算机的资源);

            ParallelOptions options = new ParallelOptions();
            options.MaxDegreeOfParallelism = 3;    //会有17个任务,限定了3个线程去执行这17个任务
            Parallel.ForEach(intlist, options, s =>
            {
                Debug.WriteLine($"index:{ s}  线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                this.DoSomething("this is Action 03");
            });
        }
    }
    Debug.WriteLine($"****************btnParallel_Click End || 线程ID:  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}

async/await