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,如果非要说四核,那也是假四核了。

原文地址:https://www.cnblogs.com/baozi789654/p/14655746.html