多线程 Task

1、开始线程的几种方式
2、Task.Delay 线程延迟执行
3、线程池
(1、Task.WaitAny :等待某一个线程执行完毕以后 继续往后执行
(2、Task.WaitAll:等待所有线程执行完,后执行。
4、Parallel
5、多线程异常处理
6、线程取消
7、多线程中的临时变量
8、线程安全 lock 锁
9、线程属性 如:线程ID
开始线程的几种方式


----------------------1---------------------------
 Task task = new Task(() =>
{
    this.DoSomethingLong("btntask_Click1");
});
task.Start();//开启了一个新的线程
//-------------------------2----------------------------
Task.Run(() =>
{
	this.DoSomethingLong("btntask_Click2");
});

有返回值的--------------------3--Result------------------------
Task<int> result = Task.Run<int>(() =>
{
    Thread.Sleep(2000);
    return DateTime.Now.Year;
});
int iResult = result.Result; //会卡界面等待


Task.Run<int>(() =>
 {
     Thread.Sleep(2000);
     return DateTime.Now.Year;
 }).ContinueWith(intT => //开启一个延续任务
 {
     int i = intT.Result;
 });


3  线程池
TaskFactory taskFactory = Task.Factory;
taskFactory.StartNew(() =>
 {
 	this.DoSomethingLong("btntask_Click3");
 });

Task.Delay 线程延迟执行

 Stopwatch stopwatch = new Stopwatch();
 stopwatch.Start();
Task task = Task.Delay(2000).ContinueWith(t =>  //任务在2000ms 以后执行
{
     stopwatch.Stop();
     Console.WriteLine($"{stopwatch.ElapsedMilliseconds}");
      Console.WriteLine("回调已完成");
 });

线程池TaskFactory ()
1、卡界面
Task.WaitAny :等待某一个线程执行完毕以后 继续往后执行
2、Task.WaitAll:等待所有线程执行完,后执行。
不卡界面,回调实现
ContinueWhenAny:等待某一个线程执行完毕以后 继续往后执行
ContinueWhenAll:等待所有线程执行完,后执行。

List<Task> tasksList = new List<Task>();

TaskFactory taskFactory = new TaskFactory();

tasksList.Add(taskFactory.StartNew(() => { this.Coding("小米", "系统管理"); }));
tasksList.Add(taskFactory.StartNew(() => { this.Coding("小孩", "部门管理"); }));
tasksList.Add(taskFactory.StartNew(() => { this.Coding("小吕", "客户管理"); }));
tasksList.Add(taskFactory.StartNew(() => { this.Coding("小组", "接口管理"); }));
tasksList.Add(taskFactory.StartNew(() => { this.Coding("撒的", "写Api"); }));

//可以不卡界面,提高性能
// 有没有既不卡界面,也能在所有人完成以后,做点其他别的事儿;
//这就是回调的实现
// 在这一堆任务中,只有有一个执行完毕就执行指定的任务,
taskFactory.ContinueWhenAny(tasksList.ToArray(), t => { Console.WriteLine($"只要有一个人完成,即可触发,获取一个红包奖励!"); });
taskFactory.ContinueWhenAll(tasksList.ToArray(), t => { Console.WriteLine($"等待所以只需完毕, 所有人任务开发完毕以后,大家一起庆祝一下!"); });


//如果有一个同学完成了某一个模块,老师就需要准备环境!
// 等待某一个线程执行完毕以后 继续往后执行
Task.WaitAny(tasksList.ToArray());
Console.WriteLine("老师开始准备环境部署项目!");

//老师要等待大家都完成了以后,开始给点评!
 Task.WaitAll(tasksList.ToArray()); //阻塞主线程
Console.WriteLine("5个模块均已完成,老师点评!");

// ContinueWhenAny 带传参,回调。
List<Task> tasksList = new List<Task>();
TaskFactory taskFactory = new TaskFactory();
tasksList.Add(taskFactory.StartNew((o) => { this.Coding("流光易逝", "部门管理"); }, "流光易逝"));
tasksList.Add(taskFactory.StartNew((o) => { this.Coding("偏执", "客户管理"); }, "偏执"));
tasksList.Add(taskFactory.StartNew((o) => { this.Coding("清茶", "接口管理"); }, "清茶"));
tasksList.Add(taskFactory.StartNew((o) => { this.Coding("秋陌", "写Api"); }, "秋陌"));
taskFactory.ContinueWhenAny(tasksList.ToArray(), t => { Console.WriteLine($" {Thread.CurrentThread.ManagedThreadId.ToString("00")} {t.AsyncState}获取一个红包奖励!"); });


//所调用方法
 private void Coding(string name, string projectName)
        {
            Console.WriteLine($"****************{name}Coding  Start  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");

            int iResult = 0;
            for (int i = 0; i < 100_000_000; i++)
            {
                iResult += i;
            }

            Console.WriteLine($"****************{name} Coding  End  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
        }

Parallel 对Task进一步进行了封装 .Netframework 4.5版本出来
优点:使用Parallel可以控制线程的执行顺序。

        /// <summary>
        /// Parallel 对Task进一步进行了封装 .Netframework 4.5版本出来
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnParallel_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"**************** btnParallel_Click  Start  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");
            {

                //多线程中控制执行顺序,一直是一个难题,使用Parallel可以控制线程的执行顺序。
                //Parallel并发执行了五个委托,开启了新线程,主线程参与计算,界面会阻塞
                // Task WaitAll  + 主线程
                Parallel.Invoke(() => { this.DoSomethingLong("btnParallel_Click_1"); },
                    () => { this.DoSomethingLong("btnParallel_Click_2"); },
                    () => { this.DoSomethingLong("btnParallel_Click_3"); },
                    () => { this.DoSomethingLong("btnParallel_Click_4"); },
                    () => { this.DoSomethingLong("btnParallel_Click_5"); });
            }
            {
                ParallelOptions parallelOptions = new ParallelOptions();
                parallelOptions.MaxDegreeOfParallelism = 2;


                 Parallel.For(0, 10, parallelOptions,t => this.DoSomethingLong($"btnParallel_Click_{t}"));
            }
            {
                控制线程数量
                ParallelOptions parallelOptions = new ParallelOptions();
                parallelOptions.MaxDegreeOfParallelism = 2;  //控制线程的最大数量
                //控制执行数量
                Parallel.ForEach(new int[] { 12, 13, 14, 15, 16, 17 }, parallelOptions, t => this.DoSomethingLong($"btnParallel_Click_{t}"));
            } 

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

多线程异常处理
//Task.WaitAll(taskList.ToArray()); WaitAll 之后才能捕捉到所有的异常信息

            #region 多线程异常处理
            {
                try
                {
                    List<Task> taskList = new List<Task>();
                    for (int i = 0; i < 50; i++)
                    {
                        string name = $"btnThreadCore_Click_{i}";
                        int k = i;
                        taskList.Add(Task.Run(() =>
                        {
                            //try
                            //{
                            if (k == 5)
                            {
                                throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了 ");
                            }
                            else if (k == 6)
                            {
                                throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了");
                            }
                            else if (k == 10)
                            {
                                throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了");
                            }
                            Console.WriteLine($"this is {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} Ok!");
                            //}
                            //catch (Exception)
                            //{
                            //    Console.WriteLine("异常");
                            //}

                        }));
                    };
                    //Task.WaitAll(taskList.ToArray());  //如果这里不等待 try -catch 能否捕捉到异常 ,不能
                    //只有WaitAll 之后才能捕捉到所有的异常信息 
                    //在实际开发中是不允许在子线程中出现异常的
                }
                catch (AggregateException aex)  //可以有多个Catch 在匹配异常类型的时候,先具体,然后在寻找父类
                {
                    foreach (var exception in aex.InnerExceptions)
                    {
                        Console.WriteLine(exception.Message);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
                //可以通过等待线程完成来捕捉异常!
                //工作中的建议:多线程的委托中,不允许出现异常,包一层try-catch,然后记录下相关信息;

            }
            #endregion

线程取消
CancellationTokenSource

 #region 线程取消  
            {
                //在我们实际的开发工作中,在使用多线程的时候,往往有一个线程出现异常之后,就需要停止其他的线程;
                // 在Task中开启的线程是无法从外部取消,只能自己停止自己
                //{
                //    Thread thread;
                //    thread.Abort(); //停止当前线程  
                //} 
                //全局变量
                //bool IsOK = true;
                CancellationTokenSource cts = new CancellationTokenSource(); //线程安全 
                //cts 有个状态值 IsCancellationRequested 初始化位false
                //提供一个Cancel() 方法改变IsCancellationRequested状态(不能修改)

                try
                {
                    List<Task> taskList = new List<Task>();
                    for (int i = 0; i < 50; i++)
                    {
                        string name = $"btnThreadCore_Click_{i}";
                        int k = i;
                        Thread.Sleep(new Random().Next(50, 100)); //休息五到十秒 
                        taskList.Add(Task.Run(() =>
                        {
                            try
                            {
                                if (!cts.IsCancellationRequested)
                                {
                                    Console.WriteLine($" his is {name} 开始执行 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                                }
                                if (k == 5)
                                {
                                    ///.....
                                    ///如果这里就需要种植其他线程  这里就直接Cancel
                                    //cts.Cancel();
                                    //IsOK = false;
                                    throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了 ");
                                }
                                else if (k == 6)
                                {
                                    throw new Exception($"{name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} 异常了");
                                }
                                if (!cts.IsCancellationRequested)
                                {
                                    Console.WriteLine($"this is {name} 执行结束 {Thread.CurrentThread.ManagedThreadId.ToString("00")} Ok!");
                                }
                                else
                                {
                                    Console.WriteLine($"this is {name} 取消了 {Thread.CurrentThread.ManagedThreadId.ToString("00")} Ok!");
                                }
                            }
                            catch (Exception ex)
                            {
                                cts.Cancel();//  可以根据自己的业务需求直接Cancel
                                Console.WriteLine(ex.Message);
                            }
                            // 1 创建cts 共享变量  2 try-catch 捕捉异常  3 在开启的线程中 判断Action 
                            // 2 那么如果有异常出现以后,能不能让未开启的线程就直接不开启了呢?
                            //Task Run(Action action, CancellationToken cancellationToken)
                            //cts.Token.ThrowIfCancellationRequested 
                            //1 启动线程传递token 2 异常捕捉 
                        }, cts.Token));
                    };
                    Task.WaitAll(taskList.ToArray());
                }
                catch (AggregateException aex)
                {
                    foreach (var exception in aex.InnerExceptions)
                    {
                        Console.WriteLine(exception.Message);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

            }
            #endregion

多线程中的临时变量
执行发现i==20
k 只是针对于某一次for 循环,循环20次就会有20 k,所以在次打印即为正常循环次数

 #region 多线程中的临时变量
            {
                for (int i = 0; i < 20; i++)   //for 循环很快
                {
                    Task.Run(() => // 开启线程;不会阻塞的,线程会延迟启动/执行发现i==20
                    {
                        Console.WriteLine($"btnThreadCore_Click_{i} 线程Id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                    });
                } 
                for (int i = 0; i < 20; i++)
                {
                    int k = i; //作用域 这个k 是不是只是针对于某一次for 循环,循环20次就会有20 k
                    Task.Run(() =>
                    {
                        Console.WriteLine($"btnThreadCore_Click_{k} 线程Id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                    });
                } 
            }
            #endregion

线程安全
lock 锁

            #region 线程安全 
            //线程安全:如果你的代码用多线程运行和单线程运行的结果相同;

            //线程不安全 

            //线程不安全一般出现在全局变量/共享变量/磁盘文件/静态变量/数据库的值/只要是多项去访问修改的时候,就可能会出现线程安全; 
            //磁盘文件:如果开启多个线程去操作文件,A---操作   B--也操作
            //两次执行不一样:100000, 99934 
            //1 Lock;不能锁null,不建议大家锁String; 锁 this
            //使用锁:private static readonly object Obj_Lock = new object()
            //锁的作用:排他

            //2  线程安全集合 
            // System.Collections.Concurrent.ConcurrentStack 基于线程安全  
            //3 数据分拆,避免多个线程操作同一堆数据,安全又高效率
              
            for (int i = 0; i < 100000; i++)
            {
                this.NumOne += 1;
            }

            for (int i = 0; i < 100000; i++)
            {
                Task.Run(() =>
                { 
                    lock (Obj_Lock)//可以 避免多线程并发,如果锁住以后,其实这里跟单线程基本上没啥区别;
                    {
                    this.NumTow += 1;
                    } 
                });
            }

            //我们的职责是如何解决线程安全问题:
            //Lock:锁,可以锁住一个引用,避免多个线程同时并发: 
            Console.WriteLine($"**************** btnThreadCore_Click  End  {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}  ***************");

            #endregion

        
         /// <summary>
        /// 强烈建议大家使用Object类型
        /// </summary>
        private static readonly object Obj_Lock = new object();

线程属性 如:线程ID

/// <summary>
        /// Task开启线程
        /// </summary>
        public void Tasks()
        {
            for (int i = 0; i <= 20; i++)
            {
                Task.Run(() =>
                {
                    Console.WriteLine("11111111" + DateTime.Now + "线程ID" + Thread.CurrentThread.ManagedThreadId.ToString("00"));
                    Console.WriteLine("是否背地里跑线程" + Thread.CurrentThread.IsBackground);
                    int WORKERtHREAD = 0;
                    int ioThread = 0;
                    ThreadPool.GetMinThreads(out WORKERtHREAD, out ioThread);
                    Console.WriteLine("最大线程:" + WORKERtHREAD + "  最大OI线程:" + ioThread);


                    int WORKERtHREADs = 0;
                    int ioThreads = 0;
                    ThreadPool.GetAvailableThreads(out WORKERtHREADs, out ioThreads);
                    Console.WriteLine("可用线程:" + WORKERtHREADs + "  可用IO线程:" + ioThreads);
                    Console.WriteLine("");
                    Console.WriteLine("");

                    Console.WriteLine(1 + "线程ID" + Thread.CurrentThread.ManagedThreadId.ToString("00"));
                    Task.Delay(10000).Wait();
                    Console.WriteLine(2);
                });
            }

        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#中的多线程任务(Task)是一种用于并行和异步编程的重要机制。它允许我们在应用程序中创建并发任务,从而提高性能和响应性。 在C#中,我们可以使用Task类来创建和管理多线程任务。以下是使用Task进行多线程编程的一般步骤: 1. 创建一个Task对象:我们可以使用Task类的静态方法来创建一个任务。例如,可以使用Task.Run方法创建一个简单的任务。示例代码如下: ```csharp Task task = Task.Run(() => { // 在此处执行任务操作 }); ``` 2. 定义任务操作:在创建任务时,我们通常需要定义要在任务中执行的操作。可以使用lambda表达式或方法来指定任务操作。示例代码如下: ```csharp Task task = Task.Run(() => { // 在此处执行任务操作 }); ``` 3. 等待任务完成:如果需要等待任务完成,可以使用Task类的Wait方法或await关键字。这样可以确保在继续执行后续代码之前,任务已经完成。示例代码如下: ```csharp Task task = Task.Run(() => { // 在此处执行任务操作 }); task.Wait(); // 等待任务完成 // 或者使用 await 关键字: await task; ``` 4. 处理任务结果:如果任务有返回值,我们可以使用Task<T>泛型类来创建带返回值的任务,并通过Result属性获取任务的结果。示例代码如下: ```csharp Task<int> task = Task.Run(() => { // 在此处执行任务操作并返回结果 return 42; }); int result = task.Result; // 获取任务的结果 ``` 总结起来,使用C#Task类可以方便地实现多线程编程。我们可以创建、定义和等待任务,并处理任务的结果。这样可以实现并行执行任务,提高应用程序的性能和响应性。 提供了一个基于C#开发的工具类库(MSCL超级工具类库),其中包括了很多常用工具类的封装。虽然它没有提到Task类,但我们可以借助C#多线程编程机制来实现并发任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值