差不多线程学习笔记第二首。多线程和异步编程。

by admin on 2018年9月19日

   前言:这篇博客主要是在齐同样篇博客的底子及,重新复习了瞬间达成一样首博客的内容,讲解了有些不同寻常之利用,并且动用了大气之案例来证明,主要说明了异步委托的运用与回调函数执行异步委托以及跨线程访问控件的缓解方案和一个双色球的案例。这篇稿子我为代码的款式写了,里面有大量之注释,我们可以详细的钻,很欢喜能与你们并研究。

过程、应用程序域、上下文及线程之间的干:
 
进程:故来叙述一组资源及程序运行所要的内存分配,简单来说即使是一个运转程序。
  线程:经过(可尽应用程序)中的基本实行单元
  应用程序域:承载.net可执行程序的逻辑分区
  上下文:应用程序域可以让细分为多个达标下文边界
 
 1.一个.net进程可以承接多独应用程序域.每一个应用程序域可以承接多单相关的.net程序集
 2.一个加的应用程序域中含一个还是多单上下文。使用前后文CLR能拿‘有特别要求’的队象
  
放置到一个逻辑容器中,确保该对象的周转时需要会给满足。

 3.一个应用程序域中或许产生差不多个线程,而且一个特定的线程在那生命周期内并不一定被限定于
  
一个应用程序域中(线程调度使其可跨应用程序域的边际)

 4.于一定的随时,一个线程也堪走及一个一定的前后文中
 
寄的异步性:
    概念一个信托:public delegate int DelCalculate(int
x, int y);
    通过IL反编译工具得以看看委托的方式结果来以下三单:

1.线程之习

    1.Invoke()方法用来调用被摄的目标为合方式保障的法门
   
2.BeginInvoke()用于异步调用方法,返回的靶子实现了IAsyncResult接口

   
3.EndInvoke()用于获取给调用方法的回到值,需要IAsyncResult类型作为参数

  (1) 多线程Thread调用委托方的贯彻Demo

 于研委托的异步方法前,现来拘禁一下联手调用方法的落实
    同步调用方法: 
 
     public delegate int DelCalculate(int x, int y);
        static void Main(string[] args)
        {
            Console.WriteLine(“***** Synch Delegate Review
*****”);
            //输出在执行线程ID
            Console.WriteLine(“Main() invoked on thread
{0}.”,Thread.CurrentThread.ManagedThreadId);
            //同步模式下调用Add()方法
            DelCalculate cal = new DelCalculate(Add);
            int result = cal.Invoke(5, 5);
            //Add方法执行了才会执行下的代码
            Console.WriteLine(“Doing more work in Main()”);
            Console.WriteLine(“result is {0}”,result);
            Console.ReadLine();
        }

       class Program

 

       {

        private static int Add(int x, int y)
        {
            //输出在执行线程ID
            Console.WriteLine(“Add() invoked on thread {0}.”,
Thread.CurrentThread.ManagedThreadId);
            //模拟一个耗时操作
            Thread.Sleep(5000);
            return x + y;
        }
 
    输出结果为:

              static void Main(string[] args)

    ***** Synch Delegate Review *****
    Main() invoked on thread 1
    Add() invoked on thread 1
    Doing more work in Main()
    result is 10
  
  可以望线程ID是同一的(所有的天职还出于主线程完成),并且实施Add方法时见面产生5秒的刹车
  
 异步调用方法:
 
     static void Main(string[] args)
        {
            Console.WriteLine(“***** Synch Delegate Review
*****”);
      //输出在执行线程ID
            Console.WriteLine(“Main() invoked on thread
{0}.”,Thread.CurrentThread.ManagedThreadId);
      //在浅线程中调用ADD方法
            DelCalculate cal = new DelCalculate(Add);
      //BeginInvoke有4个参数,前少单如和ADD方法匹配,后少独可为空
            IAsyncResult iftAR = cal.BeginInvoke(5, 5,null,null);
      //主线程开其他工作
            Console.WriteLine(“Doing more work in Main()”);
   
      //获取ADD方法的结果
            int result = cal.EndInvoke(iftAR);
            Console.WriteLine(“result is {0}”,result);
            Console.ReadLine();
        }

              {

  输出结果也:
  
      ***** Synch Delegate Review *****
      Main() invoked on thread 1
    Doing more work in Main()
      Add() invoked on thread 3
      result is 10
   
  除了ID不同外,只要程序一样运作信息 Doing more work in
Main()会及时显示出来。
  存在的题材:主线程输出 Doing more work in
Main() 消息后抑会处在阻塞状态直到次线程方法漫天到位
  
  a.IAsyncResult接口提供了IsCompleted属性,可以透过是特性来判断异步调用(即破线程)是否完成
             

                     Thread thread = new Thread(new
ThreadStart(DemoMethod));

           DelCalculate cal = new DelCalculate(Add);

      IAsyncResult iftAR = cal.BeginInvoke(5, 5,null,null);
   
            style=”color: #008000″>//在ADD方法就前会直接输出
            while (!iftAR.IsCompleted)
            {
                Console.WriteLine(“Doing more work in Main()”);
                Thread.Sleep(1000);
            }

      int result = cal.EndInvoke(iftAR);           

     Console.WriteLine(“result is {0}”,result);   

                     //设置线程参数

           
   ——————————————-
  
除了IsCompleted属性外,IAsyncResult接口提供了AsyncWaitHandle属性,该属性返回一个WaitHandle类
   实例,该实例公开了一个WaitOne()方法(可以指定等待时),使用该方式好兑现更灵活的等候逻辑
   
            while (!iftAR.AsyncWaitHandle.WaitOne(1000,true))
            {
                Console.WriteLine(“Doing more work in Main()”);
            }
   ——————————————-
   
  b.上面的法尚是勿敷高效,IsCompleted属性相当给主线程一直于询问次线程是否有实施了。 BeginInvoke
   
方法供了一个AsyncCallback委托的实例作为参数(就是第三个参数),当提供了是目标时,异步调用

    完成时,委托会自动调用AsyncCallback对象所指定的方
    
 

                     thread.IsBackground = true;
//后台线程,常用的都是后台线程

      private static bool isDone = false;
      static void Main(string[] args)       

      {           

       Console.WriteLine(“***** Synch Delegate Review
*****”);           

            Console.WriteLine(“Main() invoked on thread
{0}.”,Thread.CurrentThread.ManagedThreadId);   

       DelCalculate cal = new DelCalculate(Add);
            style=”color: #008000″>//传入了第三独参数一个AsyncCallback委托的实例
            IAsyncResult iftAR = cal.BeginInvoke(5, 5,new
AsyncCallback(AddComplete),null);
      //这里主线程开其他作业
            while (!isDone)
            {
                Console.WriteLine(“Doing more work in Main()”);
                Thread.Sleep(1000);
            }

            Console.ReadLine();
        }

        private static int Add(int x, int y)
        {
            Console.WriteLine(“Add() invoked on thread {0}.”,
Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(5000);
            return x + y;
        }

        static void AddComplete(IAsyncResult itfAR)
        {
            Console.WriteLine(“AddComplete() invoked on thread {0}.”,
Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(“Your addition is complete”);
      style=”color: #008000″>//AsyncCallback委托的靶子无法访问Main()中开创的寄托,故此处需要强转
      style=”color: #008000″>//AsyncResult类继承了IAsyncResult接口,并且AsyncResult类的特念属性AsyncDelegate返回了别处创建的旧异步委托的调用
            AsyncResult ar = (AsyncResult)itfAR;
       style=”color: #008000″>//(DelCalculate)ar.AsyncDelegate返回的就是Main()中开创的寄托
            DelCalculate cal = (DelCalculate)ar.AsyncDelegate;
            Console.WriteLine(“result is {0}.”,cal.EndInvoke(itfAR));

      isDone = true;       

  }  

                     //设置线程的名

           
  c.BeginInvoke()方法的尾声一个参数(object类型)允许主线程传递额外的状态信息让回调方法
  
      IAsyncResult iftAR = cal.BeginInvoke(5, 5,new
AsyncCallback(AddComplete),”calculate is over”);
   
      static void AddComplete(IAsyncResult itfAR)
            {
        //通过AsyncResult类的仅读属性AsyncState获取传入的数
        string message = (string)itfAR.AsyncState;
        Console.WriteLine(message);

                     thread.Name = “给开发人员用的”;

                  isDone = true;
            }
   
   
多线程: 
 手动创建次线程的步调:
 1.创办一个方作为线程的入口点
 2.开立一个ThreadStart/ParameterizedThreadStart委托,并把上同一步所定义方法的地址传递让寄的构造函数
 3.创建一个Thread对象,并将ThreadStart/ParameterizedThreadStart委托作构造函数的参数
 4.立自由初始化线程的特色(名称、优先级等)
 5.调用Thread.Start()方法
 
 行使ThreadStart委托:该信托仅仅针对无返回值、无参数的方法
 
    static void Main(string[] args)
         {
            Thread primaryThread = Thread.CurrentThread;
            primaryThread.Name = “Primary”;

                     //设置线程的优先级(只是提议)

            Console.WriteLine(“–>{0} is executing Main()”,
Thread.CurrentThread.Name);

                       thread.Priority = ThreadPriority.Highest;
//0-31

            Printer p = new Printer();
       //第二步创建一个ThreadStart委托
            ThreadStart start = new ThreadStart(p.PrintNumbers);
       //第三步创建一个Thread对象
            Thread newThread = new Thread(start);
      //第四步
            newThread.Name = “Secondary”;
       //第五步
            newThread.Start();
           
            MessageBox.Show(“I am busy!”, “Work on main thread..”);
        }
  
    public class Printer
        {
       public void PrintNumbers() //第一步创建一个方作为线程的入口点
       {
          Console.WriteLine(“–>{0} is executing
PrintNumbers()”,Thread.CurrentThread.Name);
          Console.WriteLine(“Your Numbers: “);
          for (int i = 0; i < 10; i++)
          {
             Console.WriteLine(“{0}, “,i);
             Thread.Sleep(2000);
          }
          Console.WriteLine();
       }
    }
  
 运用ParameterizedThreadStart委托:接受一个Object类型参数,无返回值
 
            Printer p = new Printer();
            ParameterizedThreadStart start = new
ParameterizedThreadStart(p.PrintNumbers);
            Thread newThread = new Thread(start);
            newThread.Start(10);
            newThread.Name = “Secondary”;
            MessageBox.Show(“I am ParameterizedThreadStart!”);
   
       public void PrintNumbers(object num)
            {
        Console.WriteLine(“–>{0} is executing PrintNumbers()”,
Thread.CurrentThread.Name);
        Console.WriteLine(“Your Numbers: “);
        //出于测试目的此处强转不审慎
        for (int i = 0; i <(int)num; i++)
        {
           Console.WriteLine(“{0}, “, i);
           Thread.Sleep(2000);
        }
        Console.WriteLine();
           }

                    
//启动线程,只是告诉操作系统线程准备好,开始实施是线程,但是并不一定线程立即施行

前台线程:能阻止应用程序的终止,直到有的前台线程都止后,CLR才能够关闭应用程序

                     thread.Start();

后台线程:被CLR认为是程序执行中但是作出牺牲之部分,即当其余时刻都可让忽视 

                     Thread.Sleep(3000);

起问题:在构建多线程应用程序时,需要保证其他共享数据都处被保安状态,以备多只线程修改外的价
               
(当有三三两两单线程访问同一共享数据时,由于线程调度器会天天挂于线程,所以若A正于修改共享

                 
数据只是还不曾完,这时B又来拜会此时A被吊起于,由于A还尚无到位所以此时B访问的虽是不当的数目) 

                     Console.ReadKey();

 1.使用C#的Lock关键字展开共同(首先方式)
    public class Printer
    {
        private object threadLock = new object();
        public void PrintNumbers()
        {
            //如果无Lock那么当有多单线程同时做客这办法时输出的或是就是是反常的屡屡
            lock(threadLock)
            {
                Console.WriteLine(“–>{0} is executing
PrintNumbers()”, Thread.CurrentThread.Name);
                Console.WriteLine(“Your Numbers: “);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(“{0}, “, i);
                    Thread.Sleep(1000);
                }
                Console.WriteLine();
            }
        }
    }
 
 Main函数中如下:
 

              }

       Printer p = new Printer();
      
//创建10独线程使该都看p.PrintNumbers()方法
            Thread[] threads = new Thread[10];
            for (int i = 0; i < 10; i++)
            {
                threads[i] = new Thread(new
ThreadStart(p.PrintNumbers));
                threads[i].Name = string.Format(“Worker thread
#{0}”, i);
            }

   foreach (Thread item in threads)            {               
item.Start();            }   

              private static void DemoMethod()

     2.用到System.Threading.Monitor类型进行协同
 
      public class Printer
      {
        private object threadLock = new object();
        public void PrintNumbers()
        {
            Monitor.Enter(threadLock);
             try
            {
                Console.WriteLine(“–>{0} is executing
PrintNumbers()”, Thread.CurrentThread.Name);
                Console.WriteLine(“Your Numbers: “);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(“{0}, “, i);
                    Thread.Sleep(1000);
                }
                Console.WriteLine();
            }
            finally
            {
                Monitor.Exit(threadLock);
            }
        }
      }

                {

 

                     Console.WriteLine(“执行的工作线程是:{0}”,
Thread.CurrentThread.ManagedThreadId); ;

3.使用[Synchronization]特色开展同步(类级别特性)
 

              }

    style=”color: #008000″>//此时Printer类中持有的道还是线程安全之
    [Synchronization]
    public class Printer:ContextBoundObject  style=”color: #008000″> //必须继承ContextBoundObject类
    {
        private object threadLock = new object();
        public void PrintNumbers()
        {
           
                Console.WriteLine(“–>{0} is executing
PrintNumbers()”, Thread.CurrentThread.Name);
                Console.WriteLine(“Your Numbers: “);
                for (int i = 0; i < 10; i++)
                {
                    Console.WriteLine(“{0}, “, i);
                    Thread.Sleep(1000);
                }
                Console.WriteLine();
         }
     }

      }

 
C#中的async和await关键字

       执行结果是:执行的干活线程是:10

 先创建一个Windows
Form应用程序,在窗体上加加一个Button和TextBox控件,代码如下
 

  (2)线程配合Lambda表达式的使

   private void btnCallMethod_Click(object sender, EventArgs e)
        {
            this.Text = DoWork();
        }

  private string DoWork()       

   {            

      Thread.Sleep(5000);           

      return “Done with work!”;      

        }  

       static void Main(string[] args)

       
  当单击了BTN按钮后,需要等待5秒钟才会以TextBox控件被输入文字,主窗体才能够为拖动
  
 使用async和await关键字对上述代码进行修改
 

     {

    private async void btnCallMethod_Click(object sender, EventArgs
e)
        {
      //以async标记了得办法,如果中尚未await方法的调用,则实在要闭塞的合的
            this.Text = await DoWork();  
        }
    //DoWork方法直接返回Task<string>对象(Task.Run()的归来值,该方法接受Func或Action委托)
        private Task<string> DoWork()
        {
            return Task.Run(() =>
            {
                Thread.Sleep(5000);
                return “Done with work!”;
            });
         }
  
 上述异步实现啊堪由此手动创建多线程来实现,但是用async和await关键字会更简明
  
 异步方法里可发差不多个await方法
 

        //第一栽写法

    private async void btnCallMethod_Click(object sender, EventArgs
e)
        {
            this.Text = await DoWork();
      //第二个await方法,还可有双重多只
            await Task.Run(() =>
            {
                Thread.Sleep(5000);
                MessageBox.Show(“Hello World1”);
            });
        }

        private Task<string> DoWork()
        {
            return Task.Run(() =>
            {
                Thread.Sleep(5000);
                return “Done with work!”;
            });

       Thread thread = new Thread(new ThreadStart(() =>

     

      {

———————-参考学习书本《精通C#》——————-

          Console.WriteLine(“执行的做事线程是:{0}”,
Thread.CurrentThread.ManagedThreadId); ;

  

       }));

       //第二种植写法

      new Thread(() =>

      {

           Console.WriteLine(“执行的工作线程是:{0}”,
Thread.CurrentThread.ManagedThreadId); ;

       }).Start();

      //设置线程参数

       thread.IsBackground = true; //后台线程,常用之且是后台线程

       //设置线程的名

       thread.Name = “给开发人员用的”;

       //设置线程的优先级(只是提议)

       thread.Priority = ThreadPriority.Highest; //0-31

   
   //启动线程,只是告诉操作系统线程准备好,开始实行这个线程,但是并不一定线程立即执行

        thread.Start();

        Thread.Sleep(3000);

       Console.ReadKey();

  }

       //执行结果是:

            //执行之干活线程是:4

            //执行之做事线程是:3

  (3)带参数的线程执行

       static void Main(string[] args)

     {

        //定义带参数的线程

        Thread thread = new Thread(new
ParameterizedThreadStart(ParameterMethodDemo));

        //开启线程,传递参数

        thread.Start(“韩迎龙”);

        Thread.Sleep(3000);

      }

      private static void ParameterMethodDemo(object obj)

      {

         Console.WriteLine(“执行一个带参数的线程方法:{0}”, obj);

      }

      //执行结果:执行一个牵动参数的线程方法:韩迎龙

  (4)异步委托回调函数的使

       namespace ReviewThread

       {

              //定义委托

              internal delegate int AddDel(int a,int b);

             class Program

              {

                     static void Main(string[] args)

                     {

                           
//定义委托变量的时段,如果是靠为第一单方法,那么得利用new关键字

                            AddDel del = new AddDel(Demo);

                            //同步调用

                            //int res = del(5, 3);

                           
//异步调用,本质就是重新开一个线程帮我们,执行委托的主意

                            //内部:开启一个初的线程执行委托

                            IAsyncResult result = del.BeginInvoke(2,
5, null, null);

                              //执行主线程要干的事体

                            //异步委托的利益是:不死主线程

                            int methodRes = del.EndInvoke(result);

                            Console.WriteLine(methodRes);

                            Console.ReadKey();

                       }

                     public static int Demo(int a, int b)

                     {

                            return a + b;

                     }

                     //执行结果:7

              }

       }

  1. 寄的异步调用:由另外一个线程执行委托的法

  (1) 简单的异步委托调用Demo

     namespace AsyncDelegate

     {

    internal delegate int AddDemoDel(int a,int b);

            class Program

            {

                   static void Main(string[] args)

                   {

                      Console.WriteLine(“当前主线程是:{0}”,
Thread.CurrentThread.ManagedThreadId);

                      AddDemoDel addDemoDel = new AddDemoDel(AddDemo);

                           //委托的异步调用

                            
//开始调用委托方,自动创建一个线程并执行委托的道

                             IAsyncResult result =
addDemoDel.BeginInvoke(4, 5, null, null);

                             //得到委托的返回值

                             int inta = addDemoDel.EndInvoke(result);
//阻塞当前主线程,只相当于到子线程执行就,并回方法的返回值之后继续尽

                             Console.WriteLine(“主线程执行了,结果是” +
inta);

                             Thread.Sleep(10000);

                             Console.ReadKey();

                      }

                      private static int AddDemo(int a, int b)

                      {

                             Thread.Sleep(3000);

                             Console.WriteLine(“当前推行的线程是:{0}”,
Thread.CurrentThread.ManagedThreadId);

                             return a + b;

                      }

                      //执行结果:

                          //当前主线程是:9

                          //当前执行之线程是:10

                          //主线程执行完毕,结果是9

               }

            }

  (2)设置一个回调函数执行异步委托

            namespace AsyncDelegate

            {

               internal delegate int AddDemoDel(int a,int b);

               class Program

               {

                      static void Main(string[] args)

                      {

                             Console.WriteLine(“当前主线程是:{0}”,
Thread.CurrentThread.ManagedThreadId);

                             AddDemoDel addDemoDel = new
AddDemoDel(AddDemo);

                             //指定回调函数,处理的异步委托

                             addDemoDel.BeginInvoke(3, 4, new
AsyncCallback(DelCallBackFuncDemo), “韩迎龙”);

                             Console.WriteLine(“主线程执行了”);

                             Console.ReadKey();

                      }

                      //定义一个寄托单额回调方法

                      public static void
DelCallBackFuncDemo(IAsyncResult arr)

                      {

                            
Console.WriteLine(“回调函数执行的线程是:{0}”,
Thread.CurrentThread.ManagedThreadId);

                            
//当我们的嘱托方执行到位后,会活动调用次回调函数

                             AsyncResult result=(AsyncResult)arr;

                            
Console.WriteLine(“在回调方法中得到到之传递过来的状态:{0}”,result.AsyncState);

                             //回调函数获取当前托方执行之结果

                             AddDemoDel del =
(AddDemoDel)result.AsyncDelegate; //将异步委托转成我们从定义的委托项目

                             int funResult = del.EndInvoke(result);
//获取方式的返回值

                            
Console.WriteLine(“获取到即法执行之结果是:{0}”, funResult);

                      }

                      private static int AddDemo(int a, int b)

                      {

                             Thread.Sleep(3000);

                             Console.WriteLine(“当前推行的线程是:{0}”,
Thread.CurrentThread.ManagedThreadId);

                             return a + b;

                      }

               }

            }

            //执行结果如图所示:

      图片 1

  1. 一个双色球变化之案例

  (1)
创建一个Windows窗体应用程序(Winform),起名为DoubleBallDemo,拖放一个Button控件

  (2)项目Demo代码结果如下:

   namespace DoubleBallDemo

     {

            //定义设置标签值的委托类型

            public delegate void SetLbTextDel(string str,Label label);

            public partial class Form1 : Form

            {

                   List<Label> listlabel = new List<Label>();

                   public bool ISStop = true; //是否结束,改变Label的值

                   public Form1()

                   {

                          InitializeComponent();

                          //设置当前的按钮的名字为开始

                          btnStartEnd.Text = "开始";

                          //初始化6个Label标签

                          for (int i = 0; i < 6; i++)

                          {

                                 Label lblDemo = new Label();

                                 lblDemo.Text = "0";

                                 lblDemo.AutoSize = true; //自动调整大小

                                 lblDemo.Location = new Point(10 + i * 50, 50);//在X轴上每隔50放一个控件

                                 //第一将当前控件放到窗体里面去

                                 Controls.Add(lblDemo);

                                 //往当前集合变量里面添加当前标签

                                 listlabel.Add(lblDemo);

                          }

                   }

                   private void btnStartEnd_Click(object sender, EventArgs e)

                     {

                          //现在点击的是开始按钮

                          if (btnStartEnd.Text.Equals("开始"))

                          {

                                 this.ISStop = false;

                                   //启动一个线程,帮我们改变标签的数字

                                 Thread thread = new Thread(() =>

                                 {

                                        Random rand = new Random();

                                        while (!ISStop)

                                        {

                                                 for (int i = 0; i < listlabel.Count; i++)

                                               {

                                                      //listlabel[i].Text = rand.Next(0, 9).ToString();

                                                      //使用Invoke方法

                                                      //Invoke方法内部会沿着控件创建的路径往上找到创建此控件的线程,找到线程之后会直接使用此线程调用委托方法

                                                      //谁创建的此标签,那么就由谁调用后面传过来的委托

                                                      //标志:标志当前的控件是否是当前线程创建的

                                                      if (listlabel[i].InvokeRequired)

                                                      {

                                                             listlabel[i].Invoke(new SetLbTextDel(SetLbText), rand.Next(0, 9).ToString(), listlabel[i]);

                                                      }

                                                      else

                                                      {

                                                             //如果当前控件直接就是当前线程创建的那么就可以直接访问就行了

                                                             SetLbText(rand.Next(0, 9).ToString(), listlabel[i]);

                                                      }

                                               }

                                               Thread.Sleep(200);

                                        }

                                        //线程的方法执行完成后,会自动退出线程,线程的资源会自动释放

                                 });

                                 thread.IsBackground = true;

                                 thread.Start();

                                 //把当前按钮的名字改成结束

                                 btnStartEnd.Text = "结束";

                          }

                          else

                          {

                                 //停止线程,改变Laebl的数据

                                 //尽量避免直接关闭一个线程:abort方法

                                 //关闭线程,一般使用标志。

                                 this.ISStop = true;

                                 //把当前的按钮改成开始

                                 btnStartEnd.Text = "开始";

                          }

                   }

                   private static void SetLbText(string str, Label label)

                   {

                          label.Text = str;

                   }

            }

     }

(2) 项目显示截图:

    图片 2     
 图片 3

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图