C#中创建线程的四种⽅式
⽂章⽬录
前⾔
我们利⽤线程是为了更好的利⽤多核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)
{java线程池创建的四种
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只能锁对象,引⽤类型
死锁问题
这篇博客总结的很好
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论