线程 线程池 Task
程序员文章站
2022-07-02 14:36:47
首先声明 这是读了 愉悦的绅士 文章 《菜鸟之旅——学习线程(线程和线程池)》 《Task与线程》 的一些个人总结,还是那句话,如有不对,欢迎指正 文章以代码加注释的方法展示。 //线程的创建,阻塞和同步 //对方法加锁 //线程池 //Task 任务 推荐使用任务来做多线程的,便于管理 ......
首先声明 这是读了 愉悦的绅士 文章
《Task与线程》
的一些个人总结,还是那句话,如有不对,欢迎指正
文章以代码加注释的方法展示。
//线程的创建,阻塞和同步
public static ManualResetEvent MREstop=new ManualResetEvent(false); public static AutoResetEvent AREstop = new AutoResetEvent(false); static void Main(string[] args) { //使用方法注册 Thread Thread1 = new Thread(Method1); //使用Lambda注册 Thread Thread2 = new Thread((s) => { //暂停线程2,使用ManualResetEvent暂停,当使用Set方法的时候会跳过所有WaitOne(); //MREstop.WaitOne(); //暂停主线程,使用AutoResetEvent暂停,当使用Set方法的时候会跳过第一次遇到的WaitOne(); AREstop.WaitOne(); Console.WriteLine("----这是带参数方法2,参数为{0}----",s); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法2结束----"); }); //若直接运行,会发现,Thread1和主线程的代码会交错在一起,而Thread2的代码一直在最后出现,这是因为Thread1和主线程一起运行,而Thread2延迟运行 Thread1.Start(); Thread2.Start("这是一个参数"); //取消注释,会发现Thread1和Thread2都执行完后,才会执行主线程代码 //Thread1.Join(); //Thread2.Join(); //暂停主线程,使用ManualResetEvent暂停,当使用Set方法的时候会跳过所有WaitOne(); //MREstop.WaitOne(); //暂停主线程,使用AutoResetEvent暂停,当使用Set方法的时候会跳过第一次遇到的WaitOne(); //AREstop.WaitOne(); Console.WriteLine("----这是主线程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主线程结束----"); } static void Method1() { Thread.Sleep(1000); Console.WriteLine("----这是不带参数方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1结束----"); //使用线程1开启同步,当使用Set方法的时候会跳过所有WaitOne(); //MREstop.Set(); //使用线程1开启同步,,当使用Set方法的时候会跳过第一次遇到的WaitOne(),所以主要是看Cpu先执行那个进程; //AREstop.Set(); }
//对方法加锁
static readonly object LockObject = new object(); static int i = 100; static void Main(string[] args) { //实例化100条线程,执行同一个方法 for (int i = 0; i < 100; i++) { Thread Thread1 = new Thread(Method1); Thread1.Start(); } } static void Method1() { //若不加锁,所有线程都可以同时访问该方法,会造成显示的结果混乱,而加了锁,就同时只能拥有一个线程访问该方法 //Monitor.Enter(LockObject); //i++非原子性操作,可能同时被多个线程执行,造成竞态,会影响运算结果,所以不能在多线程中使用。 //i++; //推荐使用线程原子性自增操作 System.Threading.Interlocked.Increment(ref i); Thread.Sleep(10); Console.WriteLine("This is Thread{0} and i={1}", Thread.CurrentThread.ManagedThreadId, i); Console.WriteLine("--------------------------------"); //加了锁必须解锁 //Monitor.Exit(LockObject); //或者使用lock(LockObject)的方法,相当于try{Monitor.Enter(LockObject);}catch{}finally{Monitor.Exit(LockObject);}的简便写法 //lock(LockObject) //{ // System.Threading.Interlocked.Increment(ref i); // Thread.Sleep(10); // Console.WriteLine("This is Thread{0} and i={1}", Thread.CurrentThread.ManagedThreadId, i); // Console.WriteLine("--------------------------------"); //} }
//线程池
public static AutoResetEvent AREstop1 = new AutoResetEvent(false); static void Main(string[] args) { AutoResetEvent AREstop2 = new AutoResetEvent(false); //创建并且执行,线程池上限为CPU核心数*250,默认为后台线程 ThreadPool.QueueUserWorkItem(new WaitCallback(Method1), AREstop2); //创建并且执行 ThreadPool.QueueUserWorkItem(new WaitCallback(s => { Thread.Sleep(2000); Console.WriteLine("----这是带参数方法2,参数为{0}----", s); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法2结束----"); AREstop1.Set(); }), "这是一个参数"); //线程池的同步线程和线程一致,可以使用ManualResetEvent和AutoResetEvent执行。 //由于线程池没有Join方法,所以可以使用WaitAll()方法来达到所有线程执行完毕后执行主线程的效果 List<WaitHandle> handles = new List<WaitHandle>(); handles.Add(AREstop1); // handles.Add(AREstop2); //注意,对多个线程要使用不同的AutoResetEvent,只要数组中的AutoResetEvent接受到set指令就解锁,若全部为同一个名字 //则只要任何一个进程set之后,就会执行主线程。由于线程池默认为后台线程,一旦执行完成主线程,则其余线程自动结束 //必须数组之中的AutoResetEvent全部set后才会执行,如果该有一个没有set,都不会执行主线程。 //WaitAll最大数组上限为64 WaitHandle.WaitAll(handles.ToArray()); Console.WriteLine("----这是主线程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主线程结束----"); } //方法要带一个参数 static void Method1(object obj) { Thread.Sleep(1000); Console.WriteLine("----这是带参数方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1结束----"); AutoResetEvent AREstop2 = (AutoResetEvent)obj ; AREstop2.Set(); }
//Task 任务 推荐使用任务来做多线程的,便于管理
public static AutoResetEvent AREstop1 = new AutoResetEvent(false); static void Main(string[] args) { //Task实例化的都是后台线程,如果要更改为前台线程,需要再方法里面修改 #region Task任务 使用线程池 //{ // //实例化任务,必须手动启动,注意,方法是不能带参数的 // Task TaskFirst = new Task(Method1); // //Status可以标识当前任务的状态 // //Created:表示默认初始化任务,但是“工厂创建的”实例直接跳过。 // //WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。 // //RanToCompletion:任务执行完毕。 // Console.WriteLine("TaskFirst的状态:{0}", TaskFirst.Status); // TaskFirst.Start(); // Console.WriteLine("TaskFirst的状态:{0}", TaskFirst.Status); // //工厂创建的直接执行 // Task TaskSecond = Task.Factory.StartNew(() => // { // Console.WriteLine("----这是不带参数方法2----"); // Console.WriteLine(DateTime.Now); // Console.WriteLine("----方法2结束----"); // }); // //使用这种方法删除任务 // //CancellationTokenSource cancelTokenSource = new CancellationTokenSource(); // //Task.Factory.StartNew(() => // //{ // // Console.WriteLine("----这是要删除方法4----"); // // Console.WriteLine(DateTime.Now); // // Console.WriteLine("----要删除方法结束----"); // //}, cancelTokenSource.Token); // //cancelTokenSource.Cancel(); // //流程控制 // { // //没有加标识的默认使用线程池创建,若主线程结束自动结束,所以需要先堵塞主线程 // //AREstop1.WaitOne(); // //或者使用阻塞 // Task.WaitAll(TaskFirst, TaskSecond); // //也可以使用Wait()等待单个线程,你会发现下面TaskFirst的状态的状态为Running,因为主线程开始运行了,而线程TaskFirst还在运行中 // //TaskSecond.Wait(); // //Task.WaitAny 只要数组中有一个执行完毕,就继续执行主线程 // //Task.WaitAny(TaskFirst, TaskSecond); // //继续执行,在TaskFirst任务结束后继续执行,此时TaskFirst已经结束。记得加Wait(),否则主线程结束就直接结束了。 // TaskFirst.ContinueWith(NewTask => // { // Console.WriteLine("----这是不带参数方法3----"); // Console.WriteLine(DateTime.Now); // Console.WriteLine("TaskFirst的状态:{0}", TaskFirst.Status); // Console.WriteLine("----方法3结束----"); // }).Wait(); // } // Console.WriteLine("TaskFirst的状态:{0}", TaskFirst.Status); //} #endregion #region Task任务 使用线程 { ////实例化任务,必须手动启动,注意,方法是不能带参数的 //Task TaskFirst = new Task(Method1, TaskCreationOptions.LongRunning); //TaskFirst.Start(); } #endregion #region Task任务 带参数 { Task<int> TaskFirst = new Task<int>(((x) => { return (int)(x); }), 10); TaskFirst.Start(); Console.WriteLine(" result ={0}", TaskFirst.Result); Task<string> TaskSecond = Task<string>.Factory.StartNew(new Func<object, string>(x => { return $"This is {x}"; }), 10); Console.WriteLine(" result ={0}", TaskSecond.Result); } #endregion Console.WriteLine("----这是主线程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主线程结束----"); } //C# 6.0只读赋值 static object Locker { get; } = new object(); static void Method1() { lock (Locker) { Thread.CurrentThread.IsBackground = false; Thread.Sleep(1000); Console.WriteLine("----这是带参数方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1结束----"); //AREstop1.Set(); } }
上一篇: java基础(13)、异常
下一篇: Bean管理(注解)和AOP