(5条消息) C#中创建线程的四种方式
文章目录
- 前言
- 利用委托开启线程
- 步骤
- 传递参数
- 获得函数结果
- 利用Thread类直接开启线程
- 传递参数
- 线程的优先级
- 线程的状态
- 注意事项
- 利用线程池开启线程
- 利用任务开启线程
- 连续任务
- 任务层次结构
- 线程资源争夺
前言
- 我们利用线程是为了更好的利用多核pu的资源
- 有这么几个要点,
- 在线程执行完后,我们要如何获得结果
- 线程之间的资源争夺导致的问题
- 线程的执行顺序,其实线程可能由于cpu资源的动态变化,运行顺序完全随机的,不可控
- 线程开启以及消耗导致的资源浪费
- 每种创建线程的方式,给线程传递参数的方法,和获得结果的方法都不一样
利用委托开启线程
步骤
- 将一个方法赋给委托变量
- 变量调用BeginInvoke()方法 即可开启一个线程执行该方法
Func<int, int,int > a = (d, b) => d+b; IAsyncResult ar = a.BeginInvoke(3, 4,null, null);
传递参数
- BeginInvoke的前几个参数全部是传递参数,其次是回掉函数,最后是 自定义给回调函数传递的参数对象 属于为object类型
获得函数结果
- 利用while 死循环判断线程执行状态,执行结束,就取得结果
//循环判断 while (true) { if (ar.IsCompleted == true) { int r = a.EndInvoke(ar); Console.WriteLine("最后的结果是:"+r); break; } }
- 利用指定线程的等待句柄,暂停当前线程一定时间后,如果获得指定线程的响应,就返回true,否则false
Func<int, int,int > a = (d, b) => d+b; IAsyncResult ar = a.BeginInvoke(3, 4,null, null); WaitHandle handle = ar.AsyncWaitHandle; bool res = handle.WaitOne(10); //等待10s获得,如果获得响应,返回true否则false 注意他会阻塞当前线程 if (res==true) { int r = a.EndInvoke(ar); Console.WriteLine(r); }
- 利用回调函数(最优)
static void Main(string[] args) { Func<int, int,int > a = (d, b) => d+b; a.BeginInvoke(3, 4, CallBack, a); Console.ReadKey(); } static void CallBack(IAsyncResult ar) //系统会自动将该参数填充 { Func<int, int, int> b = ar.AsyncState as Func<int, int, int>; int res = b.EndInvoke(ar); Console.WriteLine("最后得到的结果是:"+ res); }
利用Thread类直接开启线程
- 位于System.Threading下面
- 可以直接new出来一个线程实例,将方法作为构造器参数
- 然后 实例.Start() 才开启线程
Thread t = new Thread(methodName);t.Start();
传递参数
- 第一种方法
- 可以在函数里面设定一个object类型的参数
- 然后在Start方法里面传递
void DoSm(Object str){ Console.WriteLine("输入的数据:"+str); }Thread t = new Thread(DoSm);t.Start("我很好")
- 第二种方法
- 可以定义一个类,类里面包含相应字段成员,然后实例化一个对象,再将对象的方法传递给线程
class Say{private string name;private int age;public void Say(string name, int age){this.name = name;this.age = age;}public void SayIt(){Console.WriteLine("我的名字是:"+name+" "+"年龄为:"+age);}}Class Program{static void main(string[] args){Say say = new Say("chaodan", 20);//创建线程Thread t = new Thread(say.SayIt);t.Start();}}
- 或者通过Lambda表达式,其参数可以访问上下文的变量
string name = "job";Thread t = new Thread( ()=>Console.WriteLine(name) );t.Start()
线程的优先级
- 在Thead类中,可以设置Priority属性,以影响线程的基本优先级 ,Priority属性是一个ThreadPriority枚举定义的一个值。定义的级别有Highest ,AboveNormal,BelowNormal 和 Lowest
线程的状态
- 获取线程的状态,当我们通过调用Thread对象的Start方法,可以创建线程,但是调用了Start方法之后,新线程不是马上进入Running状态,而是出于Unstarted状态,只有当操作系统的线程调度器选择了要运行的线程,这个线程的状态才会修改为Running状态。我们使用Thread.Sleep()方法可以让当前线程休眠进入WaitSleepJoin状态。
- 使用Thread对象的Abort()方法可以停止线程。调用这个方法,会在终止要终止的线程中抛出一个ThreadAbortException类型的异常,我们可以try catch这个异常,然后在线程结束前做一些清理的工作。
- 如果需要等待线程的结束,可以调用Thread对象的Join方法,表示把Thread加入进来,停止当前线程,并把它设置为WaitSleepJoin状态,直到加入的线程完成为止。
注意事项
- Thread创建的线程默认都是前台线程
- 可以通过设置IsBackGround来设置为后台线程
- 前台线程执行完后,会直接关闭进程,不管后台线程有没有执行完毕
- 只要还有一个前台线程没有执行完,就不会关闭进程
Thread t = new Thread(methodName);t.IsBackGround = true; //设为后台程序t.Start();
利用线程池开启线程
- 位于System.Threading 下面
- 线程池中的线程都是后台线程
- 不能把入池的线程改为前台线程
- 不能给入池的线程设置优先级或名称
- 入池的线程只能用于时间比较短的任务
- 利用线程池发起一个线程,方法必须要有一个参数
- 这和其定义的委托类型有关系
void ThreadMethod(object name){Console.WriteLine("我的名字:"+name);}ThreadPool.QueueUserWorkItem(ThreadMethod);
利用任务开启线程
- 可以new 出来一个任务实例,将方法作为构造参数传递,然后开启任务即可
- 通过TaskFctory.StartNew( TaskMethod ); 静态方法来开启
//第一种方法Task task = new Task(()=>{ Console.WriteLine("大声喊出我的名字!"); });task.Start();//第二种方法TaskFactory sd = new TaskFactory();sd.StartNew(() => Console.WriteLine(6 + 3));
连续任务
- 如果任务之间有依赖关系 可以使用连续任务
- ContinueWith的函数里面必须要有一个Task的参数
Task task = new Task(() => Console.WriteLine("你好")); task.Start(); Task task2 = task.ContinueWith((a) => Console.WriteLine("hello"));
任务层次结构
- 在一个任务中启动一个新的任务,相当于新的任务是当前任务的子任务,两个任务异步执行
- 只有子任务全部执行完了,父任务的状态才会变成RunToComPletion
线程资源争夺
- 解决办法 使用lock关键字
- 只有获得锁的线程才能操作该资源
- lock只能锁对象,引用类型
- 死锁问题
- 这篇博客总结的很好
赞 (0)