c# 异步委托和task多线程实战

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

namespace threadTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btntongbu_Click(object sender, EventArgs e)
        {
            this.AddContentTongbu($"btntongbu_Click 开始 {Thread.CurrentThread.ManagedThreadId}");
            Action<string> action = this.AddContentTongbu;
            for (int i = 0; i < 5000; i++)
            {
                action.Invoke($"btntongbu_Click_{i},ManagedThreadId= {Thread.CurrentThread.ManagedThreadId}");
            }
            this.AddContentTongbu($"btntongbu_Click 结束 {Thread.CurrentThread.ManagedThreadId}");

        }

        private void AddContentTongbu(string content)
        {
            this.textBox1.AppendText(content + "\r\n");
        }


        private void btnClearText_Click(object sender, EventArgs e)
        {
            this.textBox1.Clear();
        }

        private void DoSomeThingLongYIBU(string content)
        {
            //3.Control的Invoke和BeginInvoke 是相对于分线程(一般在分线程中调用,用来更新主线程ui)
            //Invoke立即插入主线程中执行(大致理解invoke表是同步);
            //  而BeginInvoke 表示异步,它是相对于调用线程的异步,而不是相对于UI线程的异步。
            //两者的区别就是一个导致工作线程等待,而另外一个则不会
            //Control的Invoke和BeginInvoke的参数为delegate,
            //delegate中的方法是在Control的线程中执行的,即UI线程中執行

            #region form界面还是会有卡顿
            for (int i = 0; i < 1200; i++)
            {
                //Console.WriteLine($"计数={i}");//控制台这样是异步,界面无卡顿'

                //BeginInvoke 卡斯界面,报错
                // BeginInvoke 表示异步,它是相对于调用线程的异步,而不是相对于UI线程的异步。
                //this.label1.BeginInvoke(new Action(() =>
                //{
                //    this.label1.Text = i.ToString();
                //    Application.DoEvents();
                //     Thread.Sleep(100);
                //}));

                //Invoke正常
                //this.label1.Invoke(new Action(() =>
                //{
                //    this.label1.Text = i.ToString();
                //    Application.DoEvents();  //加了后可以实时刷新label,不加则是同步
                //    Thread.Sleep(100);
                //}));

                //Invoke跟新界面是同步 如果改用BeginInvoke会卡斯界面,报错
                this.Invoke(new Action(() =>
                    {
                        this.textBox1.AppendText(content + "\r\n");
                        this.label1.Text = i.ToString();
                        Application.DoEvents(); //加了后可以实时刷新label,不加则是同步
                        Thread.Sleep(100);
                    })
                );
            }


           
            #endregion

            //控制台这样是异步,界面无卡顿
            //Console.WriteLine(" Invoke thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString());
        }


        private void DoSomeThingToXunhuan(string content)
        {
            Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***Console执行 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            //this.textBox1.AppendText不加到invoke中是不会及时更新界面的,因为这里是多线程了
            //this.textBox1.AppendText( 
            //        $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{Thread.CurrentThread.ManagedThreadId.ToString("00")}" + "\r\n");
            
            //Invoke跟新界面是同步就是回到主线程执行 如果嵌套在循环中,改用BeginInvoke会卡斯界面,报错
            this.Invoke((MethodInvoker)delegate
            {
                Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***执行 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                this.textBox1.AppendText(
                    $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{ content}" + "\r\n");
                this.label1.Text = $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{ content}";
                //如果没有加上 DoEvents的话,由于循环时间会比较久就会出现假死的状态
                //,而且程序不能处理其他的事件。而如果加上DoEvents的话就会对文本框的值实时响应
                //,给用户带来较好的用户体验,可是DoEvents也带来了效率上的问题,
                // 处理同样的一个事件调用了DoEvents后效率降低了好几倍,这也是为什么要慎用的原因,能不用尽量不用
                Application.DoEvents(); //循环中加了后可以实时刷新label,不加则是同步
                Thread.Sleep(1000);
            });
        }
       
            private void DoSomeThingYIBU02(string content)
        {
            //Invoke跟新界面是同步 如果史嵌套在循环中,改用BeginInvoke会卡斯界面,报错
            this.Invoke((MethodInvoker)delegate
            {
                Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ******开始执行");
                this.textBox1.AppendText(
                    $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{ content}" + "\r\n");
                this.label1.Text = $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ,{ content}";
                //如果没有加上 DoEvents的话,由于循环时间会比较久就会出现假死的状态
                //,而且程序不能处理其他的事件。而如果加上DoEvents的话就会对文本框的值实时响应
                //,给用户带来较好的用户体验,可是DoEvents也带来了效率上的问题,
                // 处理同样的一个事件调用了DoEvents后效率降低了好几倍,这也是为什么要慎用的原因,能不用尽量不用
                    //Application.DoEvents(); //循环中加了后可以实时刷新label,不加则是同步
                    Thread.Sleep(1000); 
            });

            //this.textBox1.BeginInvoke((MethodInvoker)delegate
            //{
            //    this.textBox1.AppendText(content + "\r\n");
            //});
        }

        /// <summary>
        /// 委托异步测试1
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnYibu_Click(object sender, EventArgs e)
        {
            this.AddContentTongbu($"btnYibu_Click 开始 {Thread.CurrentThread.ManagedThreadId}");
            Action<string> action = this.DoSomeThingLongYIBU;
            //在委托中循环
            //action.BeginInvoke=异步多线程
            action.BeginInvoke($"btnYibu_Click,ManagedThreadId= {Thread.CurrentThread.ManagedThreadId}",null,null);
            this.label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();
            this.AddContentTongbu($"btnYibu_Click 结束 {Thread.CurrentThread.ManagedThreadId}");
        }

        /// <summary>
        /// 委托异步测试2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnYibu2_Click(object sender, EventArgs e)
        {
            this.AddContentTongbu($"btnYibu_Click2 开始 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            Action<string> action = this.DoSomeThingToXunhuan;
            for (int i = 0; i < 1000; i++)
            {
                //DoSomeThingYIBU02  Thread.Sleep(100);执行一次
                //action.BeginInvoke=异步多线程
                action.BeginInvoke($"Yibu_{i},ManagedThreadId= {Thread.CurrentThread.ManagedThreadId.ToString("00")}", null, null);
               // Thread.Sleep(50);//加上就卡死
               // 这里一运行就会显示 01=999 ,因为上面是异步
                this.label2.Text = $"{Thread.CurrentThread.ManagedThreadId.ToString("00")}={i}";
            }
           
            this.AddContentTongbu($"btnYibu_Click 结束 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
        }

        /// <summary>
        /// 线程更新
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnThread_Click(object sender, EventArgs e)
        {
            this.label1.Text = "";
            this.AddContentTongbu($"btnThread_Click 开始 {Thread.CurrentThread.ManagedThreadId}");
            //启动一个线程 
            Thread thread = new Thread(new ThreadStart(ThDoWork));
            thread.IsBackground = true;
            thread.Start();

            //从ThreadPool池中取线程,ThreadPool会根据限制的数量去申请与释放
            //ThreadPool.SetMaxThreads(100);
            //ThreadPool.SetMaxThreads(10);
            WaitCallback waitCallback = o =>
            {
                for (int i = 0; i < 1000; i++)
                {
                    Console.WriteLine("waitCallback thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); 
                                                                                                          
                    //Invoke跟新界面是同步 如果实在循环内,改用BeginInvoke会卡斯界面
                    this.Invoke(new Action(() =>
                    {
                        //Invoke thread id is:1  ,,,回到UI主線程
                        Console.WriteLine("waitCallback Invoke thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString("00"));
                        this.label1.Text =  "计数"+ i.ToString();
                        this.textBox1.AppendText($"计数{i},线程ID={Thread.CurrentThread.ManagedThreadId.ToString("00")}" + "\r\n");
                        Console.WriteLine("waitCallback Invoke thread id is!" + Thread.CurrentThread.ManagedThreadId.ToString("00"));
                    }));
                    Console.WriteLine("waitCallback Done!");  
                };
                //取参数值
                object[] obj = o as object[];
                Console.WriteLine(obj[0].ToString()+" " + obj[1].ToString());

            };
            ThreadPool.QueueUserWorkItem(waitCallback,new object[]{"haha","ohye" });

            #region Parallel并发
            //Parallel可以启动多线程,但主线程也参与计算,节约一个线程
            //ParallelMergeOptions可以轻松最大并发多大并发数
            //Parallel.Invoke(
            //    () => { },
            //    () => { },
            //    () => { }
            //    );
            #endregion
        }
        public void DoWork() 
        {
            for (int i = 0; i < 1000; i++)
            {
                Console.WriteLine("thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Task thread id is: 4 ,,,新的線程
                //Invoke跟新界面是同步 如果实在循环内,改用BeginInvoke会卡斯界面
                this.Invoke(new Action(() =>
                {
                    Console.WriteLine(" Invoke thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Invoke thread id is:1  ,,,回到UI主線程
                    this.label1.Text = i.ToString();
                    this.textBox1.AppendText($"计数{i},线程ID={Thread.CurrentThread.ManagedThreadId}" + "\r\n");
                    Console.WriteLine("Invoke1!");
                }));
                Console.WriteLine("Done!"); // 上面語句如果是BeginInvoke,則這個及后面語句不會被阻塞,它的輸出在“Invoke1”,“Invoke2”之前。反之,用Invoke則會,它的輸出在之後。
            }

        }

        /// <summary>
        /// 如果是thread調用,注意参数是object。task 調用的話就無謂。
        /// </summary>
        public void ThDoWork()
        {
            Console.WriteLine("开始 thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString("00")); //Task thread id is: 4 ,,,新的線程
            //如果是执行一次可以用 BeginInvoke                                                                                    //Invoke跟新界面是同步 如果改用BeginInvoke会卡斯界面
            this.BeginInvoke(new Action(() =>
            {
                Console.WriteLine(" begin thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Invoke thread id is:1  ,,,回到UI主線程
                this.textBox1.AppendText($"begin 线程ID={Thread.CurrentThread.ManagedThreadId.ToString("00")}" + "\r\n");
                // Thread.Sleep(2000);加了延迟主线程会卡住2秒
                this.textBox1.AppendText($"end 线程ID={Thread.CurrentThread.ManagedThreadId.ToString("00")}" + "\r\n");
                Console.WriteLine(" end thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString()); //Invoke thread id is:1  ,,,回到UI主線程
            }));
            Thread.Sleep(2000);
            Console.WriteLine("结束 thread id is:" + Thread.CurrentThread.ManagedThreadId.ToString("00")); // 上面語句如果是BeginInvoke,則這個及后面語句不會被阻塞,它的輸出在“Invoke1”,“Invoke2”之前。反之,用Invoke則會,它的輸出在之後。
        }


        /// <summary>
        /// 控制委托执行顺序
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCtrlOrder_Click(object sender, EventArgs e)
        {
            this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 控制委托执行顺序开始 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            Action<string> action = this.DoSomeThingYIBU02;
            //委托执行完成后的回调函数 异步回调
            AsyncCallback asyncCallback = ar =>
            {
                Console.WriteLine($"委托执行完成后的回调函数 异步回调 {Thread.CurrentThread.ManagedThreadId.ToString("00")},参数={ar.AsyncState}");// "sunday"
                Console.WriteLine($"委托执行完成后的回调函数 异步回调 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                //放到主线程钟更新textBox1
                this.Invoke(new Action(() =>
                {
                    this.textBox1.AppendText(
                        $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 委托执行完成后的回调函数 异步回调 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
                }));
            };
            //action.BeginInvoke=异步线程
            action.BeginInvoke($@" 控制委托执行顺序 BeginInvoke", asyncCallback, "sunday");
            this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}控制委托执行顺序 结束 {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
        }

        private void btnUpLoadProcess_Click(object sender, EventArgs e)
        {
            this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 文件上传开始 {Thread.CurrentThread.ManagedThreadId}");
            Action<string> action = this.YiBuShangChuanWenian;
            //action.BeginInvoke=异步线程
           IAsyncResult asyncResult= action.BeginInvoke($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} YiBuShangChuanWenian IAsyncResult", null, null);
            int i = 0; 
            //这种方法是柱塞界面的
            while (!asyncResult.IsCompleted)//开始是false 委托执行完成后会改为true
            {
                if (i < 9)
                {
                    ShowlblProccessText( $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {++i*10}%");
                }
                else
                {
                    ShowlblProccessText($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 99%");
                }
                Thread.Sleep(200);
                //无什么没有立即跟新界面,因为主线程在忙,忙完了才更新label,需要让主线程闲下来,其他操作由子线程去完成
            }
            //这里会有200毫秒的误差延迟
            //asyncResult 执行完了以后,才会执行下面这一句
            this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 完成文件上传 {Thread.CurrentThread.ManagedThreadId}");
        }

        private void ShowlblProccessText(string msg)
        {
            Console.WriteLine($"{msg},{Thread.CurrentThread.ManagedThreadId.ToString("00")} ");
            this.Invoke(new Action(() =>
            {
                lblProccess.Text = msg;
               // Application.DoEvents();好性能,能不用尽量不用
            }));
        }

        private void YiBuShangChuanWenian(string content)
        {
            for (int i = 0; i < 20; i++)
            {
                //Invoke跟新界面是同步 如果史嵌套在循环中,改用BeginInvoke会卡斯界面,报错
                this.Invoke((MethodInvoker)delegate
                {
                    Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ******开始执行--{i}");
                    this.textBox2.AppendText(
                        $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}----{i} ,{ content}" + "\r\n");
                    this.label2.Text = content;
                    Application.DoEvents(); //加了后可以实时刷新label,不加则是同步
                    Thread.Sleep(500);
                });
            }
          

        }

        private void btnXinHaoLiang_Click(object sender, EventArgs e)
        {
            this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 文件上传开始 {Thread.CurrentThread.ManagedThreadId}");
            Action<string> action = this.YiBuShangChuanWenian2;
            //action.BeginInvoke=异步线程
            var asyncResult = action.BeginInvoke("调用接口", null, null);
            // 并发执行下面的Console.WriteLine
            Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
            Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
            Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
            Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
            //阻塞当前线程,直到收到asyncResult发出的信号量,无延迟
            asyncResult.AsyncWaitHandle.WaitOne();
            // asyncResult.AsyncWaitHandle.WaitOne(5000); //等待且做多等待5秒,超过就过,就会执行下一行代码,作用相当与超时设置,当要
            //执行5个任务时,有一个任务很慢,会影响整个流程,可以做超时控制,超时就换下一个任务。
            ShowlblProccessText($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 99%");
            //asyncResult 执行完了以后,才会执行下面这一句
            this.AddContentTongbu($@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 完成文件上传 {Thread.CurrentThread.ManagedThreadId}");
        }

        private void YiBuShangChuanWenian2(string content)
        {
            //这里不能使用 this.Invoke 和 BeginInvoke 来跟新 ,
            // 因为 asyncResult.AsyncWaitHandle.WaitOne(); 柱塞了当前线程
            //this.BeginInvoke(new Action(() =>
            //{
                Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {content}**执行1");
                this.textBox2.AppendText(
              $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}---{content}" + "\r\n");
                this.label2.Text = content;
               // Application.DoEvents(); //加了后可以实时刷新label
                Thread.Sleep(1000);
                Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {content} **执行2");
            //}));
          



        }

        /// <summary>
        /// 异步委托调用的返回值
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAsyncResult_Click(object sender, EventArgs e)
        {
            Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 开始");
            Func<int> fc = this.RemoteService;
            IAsyncResult asyncResult = fc.BeginInvoke(null, null);//asyncResult是描述异步调用状态
            int result = fc.EndInvoke(asyncResult);//获取委托异步返回值,只能用EndInvoke
            #region 也可以在回调里面拿到结果
            //简写
            Func<string, string> func2 = s => $"1+{s}";
            string result2 = func2.EndInvoke(func2.BeginInvoke("func2的字符串参数", null, null));
            Console.WriteLine($"简写 = {result2}");
            //回调
            IAsyncResult asyRes = fc.BeginInvoke(ar =>
            {
                int Result4 = fc.EndInvoke(ar);// 拿到RemoteService 的计算结果 只能在回调里面拿到结果
                Console.WriteLine($"回调 = {Result4}");
            }, null);
            //int Result5 = fc.EndInvoke(asyRes);//每个异步操作,只能使用一次EndInvoke
            #endregion
            Console.WriteLine($"result = {result}");
            Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 结束");

        }

        private int RemoteService()
        {
            long result=0;
            for (int i = 0; i < 100000000; i++)
            {
                //Console.WriteLine($"{i}");
                result += i;
            }
            return DateTime.Now.Day;
        }

        #region  task是多线程的最佳实践
        //task全部是线程池的线程
        private void btnTask_Click(object sender, EventArgs e)
        {
            // Action action = () =>
            //{
            //    Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} **执行1");
            //    this.textBox2.AppendText(
            //  $@"{ DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}" + "\r\n");
            //     // Application.DoEvents(); //加了后可以实时刷新label
            //     Thread.Sleep(1000);
            //    Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} **执行2");
            //};
            // Task task = new Task(action);
            // task.Start();

            //1,柱塞界面
            //List<Task> taskList = new List<Task>();
            //taskList.Add(Task.Run(()=> { this.Coding("AAA"); }));
            //taskList.Add(Task.Run(()=> { this.Coding("BBB"); }));
            //taskList.Add(Task.Run(()=> { this.Coding("CCC"); }));
            //taskList.Add(Task.Run(() => { this.Coding("DDD"); }));
            任一任务执行完成就执行后面的代码
            //Task.WaitAny(taskList.ToArray());
            //Console.WriteLine("有一个任务完成");
            等待所有的任务完成,柱塞界面
            //Task.WaitAll(taskList.ToArray());

            //2,这样新启动一个线程就不卡界面了,尽量不要线程套线程,会出bug
            //Task.Run(() =>
            //{
            //    List<Task> taskList = new List<Task>();
            //    taskList.Add(Task.Run(() => { this.Coding("AAA"); }));
            //    taskList.Add(Task.Run(() => { this.Coding("BBB"); }));
            //    taskList.Add(Task.Run(() => { this.Coding("CCC"); }));
            //    taskList.Add(Task.Run(() => { this.Coding("DDD"); }));
            //    //任一任务执行完成就执行后面的代码
            //    Task.WaitAny(taskList.ToArray());
            //    Console.WriteLine("有一个任务完成");
            //    //等待所有的任务完成,柱塞界面
            //    Task.WaitAll(taskList.ToArray());
            //});

            //3,不柱塞界面
            List<Task> taskList = new List<Task>();
            taskList.Add(Task.Run(() => { this.Coding("AAA"); }));
            taskList.Add(Task.Run(() => { this.Coding("BBB"); }));
            taskList.Add(Task.Run(() => { this.Coding("CCC"); }));
            taskList.Add(Task.Run(() => { this.Coding("DDD"); }));
            TaskFactory taskFactory = new TaskFactory();

            //等待任一任务完成,启动新的task来完成后续的动作
            //taskFactory.ContinueWhenAny(taskList.ToArray(), t =>
            //{
            //    Console.WriteLine($"有一个任务完成,{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            //});

            //等待所有任务完成,启动新的task来完成后续的动作
            //taskFactory.ContinueWhenAll(taskList.ToArray(), tArray =>
            //{
            //    Console.WriteLine($"任务全部完成,{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            //});

            //Continue 后面的task有可能是新线程,有可能是taskList的线程,不可能是主线程
            //线程顺序是不可预测的

            //如果想最后执行 收取费用需要这样使用taskList.Add,taskFactory.ContinueWhenAll启动新的task来完成后续的动作
            taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), tArray =>
               {
                   Console.WriteLine($"任务全部完成," +
                       $"{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
               })
            );
            Task.WaitAll(taskList.ToArray());

            Console.WriteLine($"任务全部完成,收取费用");
        }
        private void Coding(string str)
        {
            long result = 0;
            for (int i = 0; i < 200000000; i++)
            {
                result += i;
            }
            string content = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {str}*线程ID={Thread.CurrentThread.ManagedThreadId.ToString("00")}*执行";
            //this.textBox2.AppendText(content);
            Console.WriteLine(content);
        }
        #endregion

        /// <summary>
        /// 线程异常处理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnTaskMore_Click(object sender, EventArgs e)
        {
            //发生异常后,对其他线程无影响
            for (int i = 0; i < 100; i++)
            {
                string name = $"btnTaskMore_Click{i}";
                Task.Run(() =>
                {
                    try
                    {
                        if (name.Equals("btnTaskMore_Click10"))
                        {
                            throw new Exception("btnTaskMore_Click10 异常");
                        }
                        else if (name.Equals("btnTaskMore_Click15"))
                        {
                            throw new Exception("btnTaskMore_Click15 异常");
                        }
                        else if (name.Equals("btnTaskMore_Click20"))
                        {
                            throw new Exception("btnTaskMore_Click20 异常");
                        }
                        Console.WriteLine($"{name}");
                    }
                    catch (Exception ex )
                    {
                        Console.WriteLine("记录异常日志 "+ ex.Message);
                    }
                   
                });

               
            }
        }

        private static readonly object LOCK = new object();
        private void btnTaskSafe_Click(object sender, EventArgs e)
        {
            // i循环会输出5 变量临时冲突 ,k输出0,1,2,3,4
            for (int i = 0; i < 5; i++)
            {
                int k = i;//这里5次循环创建5个I变量,闭包作用
                Task.Run(() =>
                {
                    //每次循环i都是5 而k=0,1,2,3,4
                    Console.WriteLine($" this is i={i},k={k}");
                });
            }
            //多线程访问集合一般不会有安全问题,线程安全都是出在修改问题的时候。
            #region 解决线程安全问题
            List<int> intlist = new List<int>();
            for (int i = 0; i < 10000; i++)
            {
                Task.Run(() =>
                {
                    lock (LOCK)
                    {
                        intlist.Add(i);
                    }
                });

            }
            // 加lock解决线程安全问题,保证lock方法块内,任一时刻只有一个线程能进去,其他线程排队。
            Thread.Sleep(3000);
            Console.WriteLine(intlist.Count) ;
   

            #endregion
        }

        /// <summary>
        /// 线程异常处理2
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnTaskEx2_Click(object sender, EventArgs e)
        {
            
                CancellationTokenSource cts = new CancellationTokenSource();//cts.IsCancellationRequested默认false;
                List<Task> tasklist = new List<Task>();
                for (int i = 0; i < 100; i++)
                {
                    string name = $"btnTaskMore_Click{i}";
                    tasklist.Add(Task.Run(() =>
                        {
                            try
                            {
                                Thread.Sleep(new Random().Next(50, 100) * 10);
                                if (!cts.IsCancellationRequested)
                                {
                                    Console.WriteLine($"  {name} 开始执行");
                                    if (name.Equals("btnTaskMore_Click10"))
                                    {
                                        throw new Exception("btnTaskMore_Click10 异常");
                                    }
                                    else if (name.Equals("btnTaskMore_Click15"))
                                    {
                                        throw new Exception("btnTaskMore_Click15 异常");
                                    }
                                    else if (name.Equals("btnTaskMore_Click20"))
                                    {
                                        throw new Exception("btnTaskMore_Click20 异常");
                                    }
                                }
                                if (!cts.IsCancellationRequested)
                                {
                                    Console.WriteLine($"  {name} 结束执行");
                                }
                                else
                                {
                                    Console.WriteLine($"{name} 结束执行");
                                }
                            }catch(Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                                cts.Cancel();
                            }
                       

                        }  )
                    );
                }
                Task.WaitAll(tasklist.ToArray());
            
          
            
        }

        /// <summary>
        /// 线程取消
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnThreadCancel_Click(object sender, EventArgs e)
        {
            try
            {
                CancellationTokenSource cts = new CancellationTokenSource();//cts.IsCancellationRequested默认false;
                List<Task> tasklist = new List<Task>();
                for (int i = 0; i < 100; i++)
                {
                    string name = $"btnTaskMore_Click{i}";
                    tasklist.Add(Task.Run(() =>
                    {
                        try
                        {
                            Thread.Sleep(new Random().Next(50, 100) * 10);
                            if (!cts.IsCancellationRequested)
                            {
                                Console.WriteLine($"  {name} 开始执行");
                                if (name.Equals("btnTaskMore_Click10"))
                                {
                                    throw new Exception("btnTaskMore_Click10 异常");
                                }
                                else if (name.Equals("btnTaskMore_Click15"))
                                {
                                    throw new Exception("btnTaskMore_Click15 异常");
                                }
                                else if (name.Equals("btnTaskMore_Click20"))
                                {
                                    throw new Exception("btnTaskMore_Click20 异常");
                                }
                            }
                            if (!cts.IsCancellationRequested)
                            {
                                Console.WriteLine($"  {name} 结束执行");
                            }
                            else
                            {
                                Console.WriteLine($"{name} 结束执行");
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                            cts.Cancel();
                        }


                    }, cts.Token)
                    );
                }
                Task.WaitAll(tasklist.ToArray());
            }
            catch (AggregateException aex)
            {
                // CancellationTokenSource 终止线程,可以让没有启动的线程取消
                foreach (var item in aex.InnerExceptions)
                {
                    Console.WriteLine(item.Message);
                }
            }
            catch (Exception ext)
            {
                Console.WriteLine(ext.Message);
            }
        }
    }
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用异步多线程的简单示例: ```csharp using System; using System.Threading; using System.Threading.Tasks; class Program { static async Task Main() { // 使用异步编程模型创建并发执行的异步任务 Task task1 = Task.Run(async () => { await Task.Delay(1000); // 模拟耗时操作 Console.WriteLine("任务1完成"); }); Task task2 = Task.Run(async () => { await Task.Delay(2000); // 模拟耗时操作 Console.WriteLine("任务2完成"); }); // 使用多线程创建并发执行的任务 Thread thread1 = new Thread(() => { Thread.Sleep(3000); // 模拟耗时操作 Console.WriteLine("任务3完成"); }); Thread thread2 = new Thread(() => { Thread.Sleep(4000); // 模拟耗时操作 Console.WriteLine("任务4完成"); }); // 启动异步任务和线程 task1.Start(); task2.Start(); thread1.Start(); thread2.Start(); // 等待所有任务和线程完成 await Task.WhenAll(task1, task2); thread1.Join(); thread2.Join(); Console.WriteLine("所有任务和线程已完成"); } } ``` 在上面的示例中,我们使用异步编程模型创建了两个异步任务(task1 和 task2),并使用多线程创建了两个线程(thread1 和 thread2)。每个任务和线程都模拟了一些耗时操作。 通过调用 Start() 方法启动异步任务和线程,并使用 await Task.WhenAll() 方法等待所有任务和线程完成。最后输出 "所有任务和线程已完成"。 这个示例展示了如何同时使用异步编程模型和多线程来实现异步多线程。你可以根据自己的需求修改和扩展代码。 希望这个示例对你有所帮助。如果你还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值