• C#--多线程Thread和线程池ThreadPool


    以下是学习笔记:

    一、多线程的学习

    Thread基础使用-->原理分析-->ThreadPool-->Task(主流)

    二、为什么学多线程?

    在单核时代,我们写的程序,如果没有多线程,就会经常卡顿,但是并不是说用了多线程程序就绝对不卡顿。因为单核时代,即使你用

    多线程,实际上,根本不是并行执行。其实是通过时间片切换,达到“并行”目的。

    在多核时代,首先从硬件上,就已经实现了真正的并行执行,但是是不是所有的都能并行执行呢?

    三、将同步和异步多线程比较

    异步多线程:在一定程度上,能够提升系统的性能。改进用户体验。

    【1】同步效果:

     【2】异步效果:

     【3】同步和异步的代码:

            #region 同步任务
    
            private void btnSync1_Click(object sender, EventArgs e)
            {
                for (int i = 1; i < 20; i++)
                {
                    Console.WriteLine(i);
                    Thread.Sleep(200);
                }
            }
    
            private void btnSync2_Click(object sender, EventArgs e)
            {
                for (int i = 1; i < 20; i++)
                {
                    Console.WriteLine($"-------------------------{i}-----------------------");
                    Thread.Sleep(400);
                }
            }
    
            #endregion
    
            #region 异步任务
    
            private void btnExecute1_Click(object sender, EventArgs e)
            {
                Thread thread = new Thread(() =>
                 {
                     for (int i = 1; i < 20; i++)
                     {
                         Console.WriteLine(i);
                         Thread.Sleep(200);
                     }
                 });
                thread.IsBackground = true;//设置为后台线程(主程序关闭的时候,后台程序自动关闭的)
                thread.Start();
            }
         
            private void btnExecute2_Click(object sender, EventArgs e)
            {
                Thread thread = new Thread(() =>
                {
                    for (int i = 1; i < 20; i++)
                    {
                        Console.WriteLine($"-------------------------{i}-----------------------");
                        Thread.Sleep(400);
                    }
                });
                thread.IsBackground = true;
                thread.Start();
            }
    
            #endregion
    

      

    四、跨线程访问控件

    【1】效果展示

    【2】代码:

            //通过跨线程给控件赋值
            private void btnExecute1_Click(object sender, EventArgs e)
            {
                Thread thread = new Thread(() =>
                 {
                     for (int i = 0; i < 10; i++)
                     {
                         // this.lblResult1.Text = i.ToString();//报错:线程间操作无效,从不是创建控件lblResult1的线程访问它
                         //因为上面thread单独创建了一个子线程,lblResult1是在主线程中创建的,子线程不能直接操作。
    
                         if (this.lblResult1.InvokeRequired)//this.lblResult1.InvokeRequired判断需不需要跨线程来访问,这个判断可以不加的
                         {
                             this.lblResult1.Invoke(
                                 new Action<string>(data => { this.lblResult1.Text = data; }),
                                 i.ToString()
                                 );//参数1:委托,参数2:委托的参数
                             Thread.Sleep(300);
                         }
                     }              
                 });
                thread.IsBackground = true;
                thread.Start();
            }
    
            //通过线程给控件赋值
            private void btnExecute2_Click(object sender, EventArgs e)
            {
                for (int i = 0; i < 10; i++)
                {
                    this.lblResult2.Text = i.ToString();
                    Thread.Sleep(300);
                }
                    
            }
    
            //通过跨线程读取控件的值
            private void btnRead_Click(object sender, EventArgs e)
            {
                //传统方法
                //this.lblV.Text = this.txtV.Text;
    
                //如果涉及到一些查询延迟计算的,可以用跨线程
                Thread thread = new Thread(() =>
                 {
                     this.txtV.Invoke(new Action<string>(data =>
                     {
                         this.lblV.Text = data;//将读取的控件值,在其他控件lblV中显示出来
                     }), this.txtV.Text);//读取的输入:txtV
                 });
                thread.IsBackground = true;
                thread.Start();
            }
    

     

    【3】数据库访问

            //访问数据库
            private void btnExecute1_Click(object sender, EventArgs e)
            {
                Thread thread = new Thread(() =>
                {
                    string classCount = DBUtility.SQLHelper.GetSingleResult("select count(*) from Products").ToString();
                    this.lblResult1.Invoke(new Action<string>(count => { this.lblResult1.Text = count; }), classCount);
                });
                thread.IsBackground = true;
                thread.Start();
            }
            private void btnExecute2_Click(object sender, EventArgs e)
            {
    
            }
            //跨线程访问数据库,并在dgv中展示数据
            private void btnGetData_Click(object sender, EventArgs e)
            {
                Thread thread = new Thread(() =>
                {
                    DataSet ds = DBUtility.SQLHelper.GetDataSet("select * from ProductInventory;select ProductId,ProductName,Unit from Products");
                    DataTable dt1 = ds.Tables[0];
                    DataTable dt2 = ds.Tables[1];
    
                    this.dgv1.Invoke(new Action<DataTable>(t => { this.dgv1.DataSource = t; }), dt1);
                    this.dgv2.Invoke(new Action<DataTable>(t => { this.dgv2.DataSource = t; }), dt2);
                });
                thread.IsBackground = true;
                thread.Start();
            }
    

      

    五、多线程底层观察

     

    六,线程生命周期

    【1】演示:

    【2】代码

           private Thread thread = null;
            private int counter = 0;
    
            //【1】开启
            private void btnStart_Click(object sender, EventArgs e)
            {
                thread = new Thread(() =>
                 {
                     while (true)
                     {
                         try
                         {
                            Thread.Sleep(500);
                             lblInfo.Invoke(new Action(() =>
                             {
                                 lblInfo.Text += counter++ + ",";
                             }));
                         }
                         catch (Exception ex)
                         {
                             MessageBox.Show(ex.Message + "  异常位置:" + counter++);
                         }
                     }
                 });
                thread.Start();
            }
    
            //暂停(线程挂起),只能暂定正在运行的线程或休眠额线程
            private void btnSuspend_Click(object sender, EventArgs e)
            {
                if (thread.ThreadState == ThreadState.Running ||
                    thread.ThreadState == ThreadState.WaitSleepJoin)
                {
                    thread.Suspend();
                }
            }
    
            //继续(继续已挂起的线程)
            private void btnResume_Click(object sender, EventArgs e)
            {
                if (thread.ThreadState == ThreadState.Suspended )
                {
                    thread.Resume();
                }
            }
    
            //中断
            private void btnInterrupt_Click(object sender, EventArgs e)
            {
                thread.Interrupt();
            }
    
            //终止
            private void btnAbort_Click(object sender, EventArgs e)
            {
                thread.Abort();
            }
    

      

    【3】等待子线程执行完成

                Thread thread = new Thread(new ThreadStart(() =>
                {
                    Thread.Sleep(3000);
                    Console.WriteLine("这个是正在执行的子线程数据......");
                }));
    
                thread.Start();
    
               thread.Join();//会等待子线程执行完毕后,在执行下面的主线程内容。
    
                Console.WriteLine("这个是主线程的数据...");
    

      

    七,线程池

    当我们开发中,用的线程比较多的时候,就要考虑性能问题。所以,我们可以开几个线程,方便使用。

    【1】线程池的基本使用

            #region 线程池ThreadPool的基本使用
    
            static void Method11()
            {
                ThreadPool.QueueUserWorkItem((arg) =>
                    {
                        //请在这里编写实际的要处理的内容...
    
                        Console.WriteLine("Method11子线程Id:" + Thread.CurrentThread.ManagedThreadId);
                    });
                Console.WriteLine("Method11主线程Id:" + Thread.CurrentThread.ManagedThreadId);
            }
    
            //给线程传参数
            static void Method12()
            {
                ThreadPool.QueueUserWorkItem((arg) =>
                {
                    //请在这里编写实际的要处理的内容...
    
                    Console.WriteLine("Method12子线程Id:" + Thread.CurrentThread.ManagedThreadId);
                    Console.WriteLine("arg=" + arg);
    
                }, "arg具体的参数");
                Console.WriteLine("Method12主线程Id:" + Thread.CurrentThread.ManagedThreadId);
            }
    
            #endregion
    

      

    【2】线程和ThreadPool和Thread性能比较

            #region 线程和ThreadPool和Thread性能比较
    
            static void Method2()
            {
                for (int i = 1; i <= 10; i++)
                {
                    Thread thread = new Thread(() =>
                     {
                         Console.WriteLine($"子线程{i}   Id:" + Thread.CurrentThread.ManagedThreadId);
                         for (int a = 1; a <= 5; a++)
                         {
                             Console.WriteLine(a);
                         }
                     });
                    thread.Name = "线程名称:" + i;
                    thread.IsBackground = true;
                    thread.Start();
    
                    Thread.Sleep(50);
                }
            }
    
            static void Method3()
            {
                Console.WriteLine("------------------------使用线程池----------------------------");
    
                for (int i = 1; i <= 10; i++)
                {
    
                    ThreadPool.QueueUserWorkItem((arg) =>
                    {
                        Console.WriteLine($"子线程{i}   Id:" + Thread.CurrentThread.ManagedThreadId);
                        for (int a = 1; a <= 5; a++)
                        {
                            Console.WriteLine(a);
                        }
                    });
    
    
                    Thread.Sleep(50);
                }
            }
    
            #endregion
    

      

    用Thread的结果:

    子线程1   Id:3
    1
    2
    3
    4
    5
    子线程2   Id:4
    1
    2
    3
    4
    5
    子线程3   Id:5
    1
    2
    3
    4
    5
    子线程4   Id:6
    1
    2
    3
    4
    5
    子线程5   Id:7
    1
    2
    3
    4
    5
    子线程6   Id:8
    1
    2
    3
    4
    5
    子线程7   Id:9
    1
    2
    3
    4
    5
    子线程8   Id:10
    1
    2
    3
    4
    5
    子线程9   Id:11
    1
    2
    3
    4
    5
    子线程10   Id:12
    1
    2
    3
    4
    5
    

      

    用线程池ThreadPool的结果(下面展示的就是开了2个线程,最多的结果开了4个线程)

    ------------------------使用线程池----------------------------
    子线程1   Id:3
    1
    2
    3
    4
    5
    子线程2   Id:3
    1
    2
    3
    4
    5
    子线程3   Id:4
    1
    2
    3
    4
    5
    子线程4   Id:3
    1
    2
    3
    4
    5
    子线程5   Id:4
    1
    2
    3
    4
    5
    子线程6   Id:3
    1
    2
    3
    4
    5
    子线程7   Id:4
    1
    2
    3
    4
    5
    子线程8   Id:3
    1
    2
    3
    4
    5
    子线程9   Id:4
    1
    2
    3
    4
    5
    子线程10   Id:3
    1
    2
    3
    4
    5
    

      

    【3】分析上面使用线程池为什么最多就开了4个线程

    因为电脑里是4核的,4核从效率上来讲是最优的。以后开发过程中能使用线程池的就用线程池

    【4】任务管理说明:

    内核数是4,逻辑处理器是4,那这个CPU就是真4核,如果这里的内核数是2,逻辑处理器是4,那就是假4核了,实际上是双核四线程。

    有人会说,这里看到的CPU有几个框就代表几核,其实这是不准确的,这里的框是线程,不是核,如果是一个双核四线程的CPU,这里显示的就是四个框,那不能说这是个四核CPU,如果非要说四核,那也是假四核了。

  • 相关阅读:
    oo第四次作业总结
    oo第三次博客总结
    oo第二次博客总结
    oo第一次博客总结
    leetcode155-最小栈
    leetcode141-环形链表
    leetcode278-第一个错误的版本
    leetcode118-杨辉三角
    LeetCode21-合并两个有序列表
    LeetCode27-移除元素
  • 原文地址:https://www.cnblogs.com/baozi789654/p/14655746.html
Copyright © 2020-2023  润新知