C#多线程Thread与Task

 

一、Thread

1、Thread简单实例(创建、运行)

class Program
{
    static void Main(string[] args)
    {

        Thread thread = new Thread(() =>
        {
            Console.WriteLine("测试线程的资源占用情况!");
        });
        thread.IsBackground = true;
        thread.Start();
    }
}

2、Thread线程(开启、暂停、继续、中断、终止)

 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阻塞

    class Program
    {
        static void Main(string[] args)
        {
            Thread thread = new Thread(new ThreadStart(() =>
            {
                Thread.Sleep(3000);
                Console.WriteLine("这个是正在执行的子线程数据......");
            }));

            thread.Start();

           thread.Join();//会等待子线程执行完毕后,在执行下面的主线程内容。

            Console.WriteLine("这个是主线程的数据...");
        }

    }

4、Thread基本属性 

二、ThreadPool线程池

1、线程池ThreadPool的基本使用

static void Method1()
{
    ThreadPool.QueueUserWorkItem((arg) =>
        {
            //请在这里编写实际的要处理的内容...

            Console.WriteLine("子线程Id:" + Thread.CurrentThread.ManagedThreadId);
        });
    Console.WriteLine("主线程Id:" + Thread.CurrentThread.ManagedThreadId);
}

static void Method2()
{
    ThreadPool.QueueUserWorkItem((arg) =>
    {
        //请在这里编写实际的要处理的内容...

        Console.WriteLine("子线程Id:" + Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("arg=" + arg);

    }, "xiketang.ke.qq.com");
    Console.WriteLine("主线程Id:" + Thread.CurrentThread.ManagedThreadId);
}

三、Task

1、 Task的使用多线程任务的开启3种方式

//【1】通过new的方式创建一个Task对象,并启动
static void Method1_1()
        {
            Task task1 = new Task(() =>
            {
                //在这个地方编写我们需要的逻辑...

                Console.WriteLine($"new一个新的Task启动的子线程Id={Thread.CurrentThread.ManagedThreadId}");
            });
            task1.Start();
        }

//【2】使用Task的Run()方法
        static void Method1_2()
        {
            Task task2 = Task.Run(() =>
              {
                  //在这个地方编写我们需要的逻辑...

                  Console.WriteLine($"使用Task的Run()方法开启的子线程Id={Thread.CurrentThread.ManagedThreadId}");
              });
        }

        //【3】使用TaskFactory启动(类似于ThreadPool)
        static void Method1_3()
        {
            Task task3 = Task.Factory.StartNew(() =>
            {
                //在这个地方编写我们需要的逻辑...

                Console.WriteLine($"使用TaskFactory开启的子线程Id={Thread.CurrentThread.ManagedThreadId}");
            });
        }

2、Task使用Task的阻塞方式和任务延续 

        //Task各种【阻塞】方式(3个)
        static void Method1()
        {
            Task task1 = new Task(() =>
             {
                 Thread.Sleep(1000);
                 Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
             });
            task1.Start();
            Task task2 = new Task(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
            });
            task2.Start();

            //第1种方式:挨个等待
            //task1.Wait();
            //task2.Wait();

            //第2种方式:等待所有的任务完成    【推荐】
            //Task.WaitAll(task1, task2);

            //第3种方式:等待任何一个完成即可  【推荐】
            Task.WaitAny(task1, task2);

            Console.WriteLine("主线程开始运行!Time=" + DateTime.Now.ToLongTimeString());
        }


        //Task任务的延续:WhenAll 希望前面所有任务执行完毕后,再继续执行后面的线程,和前面相比,既有阻塞,又有延续。
        static void Method2()
        {
            Task task1 = new Task(() =>
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
            });
            task1.Start();
            Task task2 = new Task(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
            });
            task2.Start();

            //线程的延续(主线程不等待,子线程依次执行,如果你需要主线程也按照子线程的顺序来,请你自己把主线程的任务放到延续任务中就可以)
            Task.WhenAll(task1, task2).ContinueWith(task3 =>
             {
                 //在这里可以编写你需要的业务...

                 Console.WriteLine($"Task3子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
             });

            Console.WriteLine("主线程开始运行!Time=" + DateTime.Now.ToLongTimeString());
        }


        //Task的延续:WhenAny
        static void Method3()
        {
            Task task1 = new Task(() =>
            {
                Thread.Sleep(1000);
                Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
            });
            task1.Start();
            Task task2 = new Task(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
            });
            task2.Start();

            //线程的延续(主线程不等待,子线程任何一个执行完毕,就会执行后面的线程)
            Task.WhenAny(task1, task2).ContinueWith(task3 =>
            {
                //在这里可以编写你需要的业务...

                Console.WriteLine($"Task3子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
            });

            Console.WriteLine("主线程开始运行!Time=" + DateTime.Now.ToLongTimeString());
        }

3、Task使用Task常见枚举 TaskCreationOptions(父子任务运行、长时间运行的任务处理) 

//请大家通过Task的构造方法,观察TaskCreationOptions这个枚举的类型,自己通过F12查看
        static void Method1()
        {
            Task parentTask = new Task(() =>
             {
                 Task task1 = new Task(() =>
                  {
                      Thread.Sleep(1000);
                      Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
                  }, TaskCreationOptions.AttachedToParent);

                 Task task2 = new Task(() =>
                 {
                     Thread.Sleep(3000);
                     Console.WriteLine($"Task2子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
                 }, TaskCreationOptions.AttachedToParent);
                 task1.Start();
                 task2.Start();
             });

            parentTask.Start();
            parentTask.Wait();//等待附加的子任务全部完成。相当于Task.WaitAll(taks1,task2);
            //TaskCreationOptions.AttachedToParent如果这个枚举参数不添加,主线程会直接运行,不等待
            Console.WriteLine("主线程开始执行!Time=  " + DateTime.Now.ToLongTimeString());
        }


        //长时间的任务运行,需要采取的方法
        static void Method2()
        {
            Task task1 = new Task(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine($"Task1子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
            }, TaskCreationOptions.LongRunning);

            //LongRunning:如果你明确知道这个任务是长时间运行的,建议你加上。当然你使用Thread也是可以的。但是不要使用
            //ThreadPool,因为长时间占用不归还线程,系统会强制开启新的线程,会一定程度影响性能
            task1.Start();
            task1.Wait();

            Console.WriteLine("主线程开始执行!Time=  " + DateTime.Now.ToLongTimeString());
        }

4、Task使用Task中的取消功能:使用的是CacellationTokenSoure解决多任务中协作取消和超时取消方法

//【1】Task任务的取消和判断
        static void Method1()
        {
            //创建取消信号源对象
            CancellationTokenSource cts = new CancellationTokenSource();
            Task task = Task.Factory.StartNew(() =>
              {
                  while (!cts.IsCancellationRequested)//判断任务是否被取消
                  {
                      Thread.Sleep(200);

                      Console.WriteLine($"子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
                  }
              }, cts.Token);

            //我们在这个地方模拟一个事件产生
            Thread.Sleep(2000);
            cts.Cancel();//取消任务,只要传递这样一个信号就可以
        }


        //【2】Task任务取消:同时我们也希望做一些清理的工作,也就是取消这个动作会触发一个任务。
        static void Method2()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            Task task = Task.Factory.StartNew(() =>
            {
                while (!cts.IsCancellationRequested)
                {
                    Thread.Sleep(500);

                    Console.WriteLine($"子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
                }
            }, cts.Token);

            //注册一个委托:这个委托将在任务取消的时候调用
            cts.Token.Register(() =>
            {
                //在这个地方可以编写自己要处理的逻辑...
                Console.WriteLine("任务取消,开始清理工作......");
                Thread.Sleep(2000);
                Console.WriteLine("任务取消,清理工作结束......");
            });

            //这个地方肯定是有其他的逻辑来控制取消
            Thread.Sleep(3000);//模拟其他的耗时工作
            cts.Cancel();//取消任务
        }


        //【3】Task任务延时自动取消:比如我们请求一个远程接口,如果长时间没有返回数据,我们可以做一个时间限制,超时可以取消任务(比如微信红包退回)
        static void Method3()
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            // CancellationTokenSource cts = new CancellationTokenSource(3000);
            Task task = Task.Factory.StartNew(() =>
            {
                while (!cts.IsCancellationRequested)
                {
                    Thread.Sleep(300);

                    Console.WriteLine($"子线程Id={Thread.CurrentThread.ManagedThreadId}  {DateTime.Now.ToLongTimeString()}");
                }
            }, cts.Token);

            //注册一个委托:这个委托将在任务取消的时候调用
            cts.Token.Register(() =>
            {
                //在这个地方可以编写自己要处理的逻辑...
                Console.WriteLine("任务取消,开始清理工作......");
                Thread.Sleep(2000);
                Console.WriteLine("任务取消,清理工作结束......");
            });


            cts.CancelAfter(3000); //3秒后自动取消
        }

5、监视锁:Lock  限制线程个数的一把锁

//为什么要用锁?在多线程中,尤其是静态资源的访问,必然会有竞争

        private static int nums = 0;
        private static object myLock = new object();
        static void Method1()
        {
            for (int i = 0; i < 5; i++)
            {
                Task.Factory.StartNew(() =>
                {
                    TestMethod();
                });
            }
        }

        static void TestMethod()
        {
            for (int i = 0; i < 100; i++)
            {
                lock (myLock)
                {
                    nums++;
                    Console.WriteLine(nums);
                }
            }
        }
        //Lock是Monitor语法糖,本质是解决资源的锁定问题
        //我们锁住的资源一定是让线程可访问到的,所以不能是局部变量。
        //锁住的资源千万不要是值类型。
        //lock也不能锁住string类型。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值