C# 多线程学习笔记

异步编程:基于委托实现

1 声明委托

public delegate int MyCalculator(int num, int ms);

2 根据委托定义一个方法:

private int ExecuteTask(int num, int ms)
        {
            System.Threading.Thread.Sleep(ms);
            return num * num;
        }

3使用委托,创建委托变量(因为异步函数和回调函数都要用,所以定义成员变量)

 private MyCalculator objMyCal = null;

初始化委托变量

 public FrmCalllBack()
        {
            InitializeComponent();

            //【3】初始化委托变量
            this.objMyCal = new MyCalculator(ExecuteTask);

            //也可以直接使用Lambda表达式
            this.objMyCal = (num, ms) =>
                {
                    System.Threading.Thread.Sleep(ms);
                    return num * num;
                };
        }

4 同时执行多个任务

 //【4】同时执行多个任务
        private void btnExec_Click(object sender, EventArgs e)
        {
            for (int i = 1; i < 11; i++)//产生10个任务
            {
                //开始异步执行,并封装回调函数 objMyCal.BeginInvoke(10 * i, 1000 * i, null, i);
                objMyCal.BeginInvoke(10 * i, 1000 * i, MyCallBack, i);
                //最后一个参数 i 给回调函数的字段AsyncState赋值,如果数据很多可以定义成类或结构
            }
        }

5 回调函数

        private void MyCallBack(IAsyncResult result)
        {
            int res = objMyCal.EndInvoke(result);

            //异步显示结果:result.AsyncState字段用来封装回调时自定义的参数,object类型
            Console.WriteLine("第{0}个计算结果为:{1}", result.AsyncState.ToString(), res);
        }

异步编程总结:

1. 异步编程是建立在委托基础上的一种编程方法。
2. 异步调用的每个方法都是在独立的线程中执行。因此,本质上就是一种多线程程序,也可以说是一种“简化版本”的多线程技术。
3. 比较适合在后台运行较为耗费时间的《简单任务》,并且任务要求相互独立,任务中不应该有代码直接访问可视化控件。
4. 如果后台任务要求必须按照特定顺序执行,或者必须访问共享资源,则异步编程不适合,而应该直接采用多线程开发技

C#多线程

进程:一个正在运行的程序就是一个进程。操作系统根据进程分配各种资源(内存。。。)

线程:操作系统为了提高效率会将一个进程分成多个线程,并按照线程来分配Cpu执行时间。

线程特点:在具有多个Cpu的计算机中,可以并行进程。

Thread类:表示托管线程,每个Thread对象都代表一个托管线程,每个托管线程会对应一个函数。

多线程基本使用:

        //执行任务1
        private void btnExecute1_Click(object sender, EventArgs e)
        {
            int a = 0;
            //  ThreadStart()方法的定义:     public delegate void ThreadStart()
            Thread objThread1 = new Thread(delegate()
             {
                 for (int i = 1; i <= 20; i++)
                 {
                     Console.Write((a + i) + "  ");
                     Thread.Sleep(500);
                 }
             });

            //Thread objThread1 = new Thread(() =>
            //{
            //    for (int i = 1; i <= 20; i++)
            //    {
            //        Console.Write((a + i) + "  ");
            //        Thread.Sleep(500);
            //    }
            //});

            objThread1.IsBackground = true;//设置为后台线程(通常都要这样设置)
            objThread1.Start();
        }
        //执行任务2
        private void btnExecute2_Click(object sender, EventArgs e)
        {
            Thread objThread2 = new Thread(() =>
            {
                for (int i = 1; i <= 50; i++)
                {
                    Console.WriteLine("-------a" + i + "-------  ");
                    Thread.Sleep(100);
                }
            });
            objThread2.IsBackground = true;
            objThread2.Start();
        }

多线程使用其他线程资源

使用委托进行传值:

      private void btnExecute1_Click(object sender, EventArgs e)
        {
            int a = 0;
            //ThreadStart :表示一个委托
            Thread objThread1 = new Thread(() =>
            {
                for (int i = 1; i <= 100; i++)
                {
                    a += i;
                    if (this.lblResult1.InvokeRequired)//判断是否调动Invoke方法
                    {
                        //Invoke()方法的第一个参数是返回值为void的委托,第二个是给委托对应方法传递的参数
                        this.lblResult1.Invoke(new Action<string>(s => { this.lblResult1.Text = s; }), a.ToString());
                    }
                    Thread.Sleep(200);
                }
            });
            objThread1.IsBackground = true;
            objThread1.Start();
        }

        private void btnExecute2_Click(object sender, EventArgs e)
        {
            int a = 0;
            Thread objThread2 = new Thread(() =>
            {
                for (int i = 1; i <= 100; i++)
                {
                    a += i;
                    if (this.lblResult2.InvokeRequired)
                    {
                        this.lblResult1.Invoke(new Action<string>(s =>
                        {
                            this.lblResult2.Text = s;
                        }), a.ToString());
                    }
                    Thread.Sleep(200);
                }
            });
            objThread2.IsBackground = true;
            objThread2.Start();
        }

多线程访问数据库:

 private void btnExecute1_Click(object sender, EventArgs e)
        {
            Thread objThread1 = new Thread(() =>
                {
                    Thread.Sleep(3000);
                    string classCount = DBUtility.SQLHelper.GetSingleResult("select count(*) from StudentClass").ToString();
                    this.lblResult1.Invoke(new Action<string>(c => { this.lblResult1.Text = c; }), classCount);
                });
            objThread1.IsBackground = true;
            objThread1.Start();
          
        }
        private void btnExecute2_Click(object sender, EventArgs e)
        {
            Thread objThread2 = new Thread(() =>
            {
                Thread.Sleep(3000);
                string stuCount = DBUtility.SQLHelper.GetSingleResult("select count(*) from Students").ToString();
                this.lblResult2.Invoke(new Action<string>(s => { this.lblResult2.Text = s; }), stuCount);
            });
            objThread2.IsBackground = true;
            objThread2.Start();
        }       
        private void btnGetData_Click(object sender, EventArgs e)
        {
            Thread objThread3= new Thread(() =>
            {
                Thread.Sleep(2000);
                //string stuCount = DBUtility.SQLHelper.GetSingleResult("select count(*) from Students").ToString();
                //this.lblResult2.Invoke(new Action<string>(s => { this.lblResult2.Text = s; }), stuCount);

                //访问数据库
                DataSet ds = DBUtility.SQLHelper.GetDataSet("select *  from StudentClass;select StudentName,Gender,PhoneNumber from Students");
                DataTable dt1 = ds.Tables[0];
                DataTable dt2 = ds.Tables[1];

                this.dgv1.Invoke(new Action<DataTable>(c => { this.dgv1.DataSource = c; }), dt1);

                Thread.Sleep(2000);
                this.dgv2.Invoke(new Action<DataTable>(s => { this.dgv2.DataSource = s; }), dt2);

            });
            objThread3.IsBackground = true;
            objThread3.Start();
        }      

进程相关概念和进程读取方法:

进程:每一个独立运行的程序都是进程。操作系统有《进程管理模块》管理同时运行的多个程序。
进程和线程:一个进程可以包含若干线程。


句柄:(Handle)句柄是一个标识符,用于代表操作系统中的各种资源。比如各种窗体、CDI绘制的对象、进程和线程对象、文件等都拥有句柄。

句柄使用1:即使进程退出,有很多时候操作系统仍然保持进程句柄。操作系统句柄有限,使用中要节省,比如打开文件后,使用完毕要及时关掉。
句柄使用2:句柄可以通过Process对象的Handle属性访问,比如进程退出的代码、时间等。


总结提高:拥有图形界面的程序都有一个主窗体,此窗体也对应一个句柄。当主窗体关闭的时候进程也关闭。主窗体一般由主线程负责创建。

获取本机所有进程:

 private Process[] allProcess = null;//本机所有的进程
        private void btnGetProcessess_Click(object sender, EventArgs e)
        {
            allProcess = Process.GetProcesses(); //获取本机所有进程
            //循环显示
            this.lbProcess.Items.Clear();
            foreach (Process item in allProcess)
            {
                this.lbProcess.Items.Add(item.ProcessName);
            }
        }

    

        #region 显示当前进程基本信息和所调用的模块信息
        private void lbProcess_Click(object sender, EventArgs e)
        {
            if (this.lbProcess.Items.Count == 0) return;
            else
                this.lbModels.Items.Clear();
            Process currentProcess = allProcess[this.lbProcess.SelectedIndex];//获取当前选中的进程

            try
            {
                //获取当前进程所调用的模块(有些进程会拒绝访问)
                ProcessModuleCollection models = currentProcess.Modules;
                foreach (ProcessModule item in models)
                {
                    this.lbModels.Items.Add(item.FileName);
                }

                //显示当前进程的基本信息
                string info = @"进程的唯一标识符(Id):" + currentProcess.Id + "\r\n\r\n";
                info += "关联进程的本机句柄(Handle):" + currentProcess.Handle + "\r\n\r\n";
                info += "打开的句柄数(HandleCount):" + currentProcess.HandleCount + "\r\n\r\n";
                info += "关联进程的基本优先级:" + currentProcess.BasePriority + "\r\n\r\n";
                info += "进程启动的时间:" + currentProcess.StartTime;

                this.txtProcessInfo.Text = info;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        #endregion

关闭和开启进程;

#region 关闭和开启进程
        private void btnKill_Click(object sender, EventArgs e)
        {
            if (this.lbProcess.Items.Count == 0 || this.lbProcess.SelectedIndex == -1) return;
            Process currentProcess = allProcess[this.lbProcess.SelectedIndex];//获取当前选中的进程

            currentProcess.Kill();//立即停止关联的进程
            currentProcess.Close();//释放进程资源

            this.lbProcess.Items.RemoveAt(this.lbProcess.SelectedIndex);
            this.lbModels.Items.Clear();
            this.txtProcessInfo.Clear();
        }
        //开启一个新的进程(如:iexplore.exe;notepad.exe;D:\abc.pptx)
        //在windows环境变量里面没有注册的,需要使用路径
        private void btnStartnew_Click(object sender, EventArgs e)
        {
            Process.Start(this.txtProcessName.Text.Trim());
        }
        //打开IE并连接到百度堂官网
        private void btnOpenIE_Click(object sender, EventArgs e)
        {
            Process.Start("iexplore.exe", "www.baidu.com");
        }

        #endregion

线程的安全:

资源争夺问题:

lock关键字可以确保一次只有一个线程调用它。
lock关键字可以简单理解成代码块的“锁”。

        #region 线程同步(可实现线程安全)

        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "--------------主线程--------------";
            Program objDemo = new Program();

            //创建一个新的线程
            Thread objNewThread = new Thread(new ThreadStart(objDemo.ActionMethod));
            objNewThread.Name = "子线程";

            objNewThread.Start();//开始子线程,并为该线程执行ActionMethod
            objDemo.ActionMethod();//让主线程执行ActionMethod


            Console.ReadLine();
        }

        private int counter = 0;

        //被线程调用的方法【未使用线程安全措施】
        //private void ActionMethod()
        //{
        //    for (int count = 1; count <= 10; count++)
        //    {
        //        counter += count;
        //        Console.WriteLine("{0}\t{1}", Thread.CurrentThread.Name, counter);
        //    }
        //}
        //使用线程安全方法
        //private void ActionMethod()
        //{
        //    //lock关键字可以确保一次只有一个线程调用它。
        //    //lock关键字可以简单理解成代码块的“锁”。
        //    lock (this)
        //    {
        //        for (int count = 1; count <= 10; count++)
        //        {
        //            counter += count;
        //            Console.WriteLine("{0}\t{1}", Thread.CurrentThread.Name, counter);
        //        }
        //    }          
        //}
        //为了方便日常使用,提供了一个方法属性来实现同样的功能。
        [MethodImpl(MethodImplOptions.Synchronized)]
        private void ActionMethod()
        {
            for (int count = 1; count <= 10; count++)
            {
                counter += count;
                Console.WriteLine("{0}\t{1}", Thread.CurrentThread.Name, counter);
            }
        }

        #endregion

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值