c# 多线程

本文深入探讨了C#中的多线程概念,从Thread的基础使用到Action委托,解释了Invoke与BeginInvoke的区别,并展示了如何利用Task进行并行编程。进一步讲解了Task的使用,包括Task.Run、Task.Factory.StartNew以及Task的回调。最后,文章介绍了async/await关键字在异步编程中的应用,以及如何通过它们实现非阻塞的线程执行。
摘要由CSDN通过智能技术生成

多线程

定义

一个程序就是一个进程,而大多数的程序都由多个线程构成,进程是程序执行的最小单位。
多进程和异步也有一定的区别,所谓的异步就是在硬件不需要通过cpu直接和内存进行数据操作的行为,而线程是由cpu进行执行的

c#中多线程的使用

Thread的基本使用

    Action<string> action = new Action<string>(o=> { 方法体(委托)});//通过Action就可以实现多线程;

public void ace() {
Action a = Func;
a.Invoke(“帅”);//执行该方法并且开启新的线程
a(“帅”);
a.BeginInvoke(null,null,null);//委托需要的参数,回调函数,状态参数

    }
  public static void Func(string s)
    {
        Console.WriteLine("s");
    }

Invoke vs BeginInvoke

再循环执行多线程的时候 invoke实际上还是同步的过程
但是Begininvoke是异步的状态

a.BeginInvoke(null,null,null);//委托需要的参数,回调函数,状态参数
状态参数可以再 线程对象的AsyncState中取到

判断 BeginInvoke 线程是否执行完成

代码重点:x.IsCompleted, x.AsyncWaitHandle.WaitOne();
方法一  x.IsCompleted:
   Action<string> a = Func;
            
            a.Invoke("帅");//执行该方法并且开启新的线程
            a("帅");
        IAsyncResult  x = a.BeginInvoke(null,null,null);//委托需要的参数,回调函数,状态参数

           if(!x.IsCompleted) {
                Console.WriteLine("!x.IsCompleted 返回的是异步线程的完成状态如果未完成返回值就为false");
            }
        
方法二   x.AsyncWaitHandle.WaitOne()WaitOne(-1)--> 一直等待  WaitOne(1000)--> 最多等待1000毫秒,1000毫秒之后自动执行后续语句
   Action<string> a = Func;
            
            a.Invoke("帅");//执行该方法并且开启新的线程
            a("帅");
        IAsyncResult  x = a.BeginInvoke(null,null,null);//委托需要的参数,回调函数,状态参数
            x.AsyncWaitHandle.WaitOne();//等待信号量,当a线程完成之后直接执行 x.AsyncWaitHandle.WaitOne();否面的语句

EndInvoke

end ---->存放的就是F执行之后的值 当b线程执行完成之后 end会得到值

public void ace() {
Func<string> b = F;
IAsyncResult asyncResult = b.BeginInvoke(null,null);
var end = b.EndInvoke(asyncResult);

//第二种写法   返回的值同样在end 中
  IAsyncResult asyncResult = b.BeginInvoke(ar=> {
                var end = b.EndInvoke(ar);
            }, null);
        }
        
  public static string F()
        {         
            Console.WriteLine("s");
            return "s";
        }

多线程的发展 无控制的Thread 1.1 到 ThreadPool 线程池 到Task

Task 穿插知识点 并行编程Parallel —>子线程在异步执行的时候主线程也参与计算 可以启动多个线程

Task全部都是线程池线程,在线程的创建上是安全的

启动线程

  Func<string> b = F;
            Task<string> T = new Task<string>(b);
            T.Start();

线程数组的执行

 static void Main(string[] args)
        {
            Console.WriteLine("线程开始");
            List<Task> tasklist = new List<Task>();
            tasklist.Add(
                Task.Run(() => { Console.WriteLine("1开始"); Thread.Sleep(200); Console.WriteLine("1结束"); }
            )) ;
            tasklist.Add(
                  Task.Run(() => { Console.WriteLine("2开始"); Thread.Sleep(200); Console.WriteLine("2结束"); }
                ) );
            tasklist.Add(
                  Task.Run(() => { Console.WriteLine("3开始"); Thread.Sleep(200); Console.WriteLine("3结束"); })
                );   
             Task.WaitAny(tasklist.ToArray());//线程中任意一个完成就返回主进程
         //   Task.WaitAll(tasklist.ToArray());//全部完成之后在返回主进程
            Console.WriteLine("线程结束");
            Console.ReadLine();
        }

Task 回调的使用 task.ContinueWith((t) => Console.WriteLine(“开始回调的执行”));

 Console.WriteLine("主线程开始");
            Task task = Task.Run(() => { Console.WriteLine("子线程开始"); Console.WriteLine("子线程结束"); });
            task.ContinueWith((t) => Console.WriteLine("开始回调的执行"));

             //List<Task> tasklist = new List<Task>();

            //tasklist.Add(Task.Run(() => EatApple("张三")));
            //tasklist.Add(Task.Run(() => EatApple("李四")));
            //tasklist.Add(Task.Run(() => EatApple("王五")));
            //TaskFactory taskfactory = new TaskFactory();
            //taskfactory.ContinueWhenAll(tasklist.ToArray(), tarray =>
            //{
            //    Console.WriteLine($"苹果全部被吃完了");
            //});
            Console.WriteLine("主线程结束");

            Console.ReadKey();

执行结果

async await

当代码执行到 await是,await后面的代码就相当于子线程的回调

1.用async来修饰一个方法,表明这个方法是异步的,声明的方法的返回类型必须为:void或Task或Task。方法内部必须含有await修饰的方法,如果方法内部没有await关键字修饰的表达式,哪怕函数被async修饰也只能算作同步方法,执行的时候也是同步执行的。
2.被await修饰的只能是Task或者Task类型,通常情况下是一个返回类型是Task/Task的方法,当然也可以修饰一个Task/Task变量,await只能出现在已经用async关键字修饰的异步方法中。

        static void  Main(string[] args)
        {
            Console.WriteLine("主线程开始");    
            F();
            Console.WriteLine("主线程结束");
            }


  public static async Task F()
        {
            
            var t = Task.Run(() => {
                Thread.Sleep(5000);
                return "Hello I am TimeConsumingMethod";
            });
            string Text = await t;//当子线程结束之后 可以使用变量接收子线程的结果带到回调中使用
            Console.WriteLine(Text);

        }
有返回值得Task
static async Task Main(string[] args)
        {
            Console.WriteLine("主线程开始");
         


          string k = await F("传入的参数");
            Console.WriteLine(k.ToString());//等到子线程执行完成之后才会返回,才能执行这句话  且不会卡界面
            Console.WriteLine("主线程结束");
            }
public static async Task<string> F(string s)
        {

            var t = Task.Run(() => {
                Thread.Sleep(5000);
                return "Hello I am TimeConsumingMethod";
            });
           
            string Text = await t;  
            Console.WriteLine(Text);
         return string.Format("{0}+处理之后的结果", s); ;

        }

多线程运行且不占用主线程 在使用线程的过程中尽量不要线程套线程

第一种方法 不推荐使用
  static void Main(string[] args)
        {
            Console.WriteLine("主线程开始");
            List<Task> tasklist = new List<Task>();
            tasklist.Add(
                Task.Run(() => { Console.WriteLine("1开始"); Thread.Sleep(200); Console.WriteLine("1结束"); }
            )) ;
            tasklist.Add(
                  Task.Run(() => { Console.WriteLine("2开始"); Thread.Sleep(200); Console.WriteLine("2结束"); }
                ) );
            tasklist.Add(
                  Task.Run(() => { Console.WriteLine("3开始"); Thread.Sleep(200); Console.WriteLine("3结束"); })
                );
            //将方法体放在新线程中,解放主线程
            Task.Run(() =>
            {
                Console.WriteLine("线程开始");
                Task.WaitAny(tasklist.ToArray());//线程中任意一个完成就返回主进程
                                                 //   Task.WaitAll(tasklist.ToArray());//全部完成之后在返回主进程
                Console.WriteLine("线程结束");
            });
            Console.WriteLine("主线程结束");
            Console.ReadLine();
        }
第二种方法 推荐使用
static void Main(string[] args)
        {
            Console.WriteLine("主线程开始");
            List<Task> tasklist = new List<Task>();
            tasklist.Add(
                Task.Run(() => { Console.WriteLine("1开始"); Thread.Sleep(200); Console.WriteLine("1结束"); }
            )) ;
            tasklist.Add(
                  Task.Run(() => { Console.WriteLine("2开始"); Thread.Sleep(200); Console.WriteLine("2结束"); }
                ) );
            tasklist.Add(
                  Task.Run(() => { Console.WriteLine("3开始"); Thread.Sleep(200); Console.WriteLine("3结束"); })
                );
            TaskFactory taskfactory = new TaskFactory();
            //等待全部任务完成之后启动一个新的线程完成后续业务
            //taskfactory.ContinueWhenAll(tasklist.ToArray(),tarray =>{
            //    Console.WriteLine("线程123已经执行完成了,可以进行后面的业务了");
            //});
            //等待任意一个任务完成之后启动一个新的线程完成后续业务
            taskfactory.ContinueWhenAny(tasklist.ToArray(), tarray =>
            {
                Console.WriteLine("线程123已经执行完成了,可以进行后面的业务了");
            });
            Console.WriteLine("主线程结束");
            Console.ReadLine();
        }

线程锁

创建线程锁

实际上线程锁就是将异步方法变同步的方式。在同一时间只有一个访问该资源的线程在执行

原理是锁定一个内存的引用地址,所以 lock (locker)的参数不可以是值类型,也不能是null
private static object locker = new object();//创建锁

锁的原则,当两个线程锁的是同一个引用类型那么他们就不能并发,当两个线程锁的变量不是同一个引用类型的话就可以并发。

apple 为公共资源,同一时间只有一个线程可以操作该资源,直到正在使用的线程释放该资源其他资源才能操作
  private static int apple = 10;              //10个苹果
        private static object locker = new object();
        static void Main(string[] args)
        {
       
            Task t1 = new Task(() => EatApple("张三"));
            Task t2 = new Task(() => EatApple("李四"));
            t1.Start();
            t2.Start();
            Console.ReadKey();
}
  private static void EatApple(string name)
        {
            while (true)
            {
                lock (locker)//加锁
                {
                    apple -= 1;
                    Console.WriteLine(name + "正在吃苹果");
                    Thread.Sleep(3000);
                    Console.WriteLine(name + "吃完了,还剩" + apple + "个苹果\n");
                    if (apple <= 1)//变为1 不然会吃-1个苹果
                        break;
                }
            }
        }

写法2:

  private static int apple = 10;              //10个苹果
        private static object locker = new object();
        static void Main(string[] args)
        {
             List<Task> tasklist = new List<Task>();

            tasklist.Add(Task.Run(() => EatApple("张三")));
            tasklist.Add(Task.Run(() => EatApple("李四")));
            tasklist.Add(Task.Run(() => EatApple("王五")));
            TaskFactory taskfactory = new TaskFactory();
            taskfactory.ContinueWhenAll(tasklist.ToArray(), tarray =>
            {
                Console.WriteLine($"苹果全部被吃完了");
            });


            Console.ReadKey();
            }
    private static void EatApple(string name)
        {
            while (true)
            {
                lock (locker)//加锁
                {                    
                    Console.WriteLine(name + "正在吃苹果");
                    apple -= 1;
                    Thread.Sleep(1000);
                    Console.WriteLine(name + "吃完了,还剩" + apple + "个苹果\n");
                    if (apple <= 2)//变为2 不然会吃-1个苹果
                        break;
                }
            }
        }

Task实例

  static void Main(string[] args)
        {
            1.new方式实例化一个Task,需要通过Start方法启动
            Task<string> task = new Task<string>(() =>
            {
                Console.WriteLine("task");
                return $"hello, task1的ID为{Thread.CurrentThread.ManagedThreadId}";
            });
            task.Start();

            2.Task.Factory.StartNew(Func func)创建和启动一个Task
            Task<string> task2 = Task.Factory.StartNew<string>(() =>
            {
                Console.WriteLine("task2");
                return $"hello, task2的ID为{ Thread.CurrentThread.ManagedThreadId}";
            });

            3.Task.Run(Func func)将任务放在线程池队列,返回并启动一个Task
            Task<string> task3 = Task.Run<string>(() =>
            {
                Console.WriteLine("task3");
                return $"hello, task3的ID为{ Thread.CurrentThread.ManagedThreadId}";
            });
// 三个线程内部同时在进行执行,但是当运行到 task.Result 时 就会等待一个线程执行完毕之后在向下执行
            Console.WriteLine("执行主线程!");
            Console.WriteLine(task.Result);
            Console.WriteLine(task2.Result);
            Console.WriteLine(task3.Result);
            Console.ReadKey();
        }

Task 同步执行

static void Main(string[] args)
        {
            Task task = new Task(() =>
            {
                Thread.Sleep(100);
                Console.WriteLine("执行Task结束!");
            });
            //同步执行,task会阻塞主线程
            task.RunSynchronously();//当task线程执行完成之后 主进程才会向后执行
            Console.WriteLine("执行主线程结束!");
            Console.ReadKey();
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值