.Net Task的用法(一)线程
在了解Task用法之前就不得不先对线程有一定的了解
线程的创建
static void Main(){
new Thread(Go).Start(); // new 一个Thread后要调用Start() 线程才会开始执行
Task.Factory.StartNew(Go); //立即创建并执行
Task.Run(new Action(Go)); // 立即创建并执行
}
public static void Go(){
Console.WriteLine("我是另一个线程");
}
线程的创建是比较占用资源,所以有了线程池,new 一个Thread 不会通过线程池(当然也可以使用ThreadPool),Task默认直接使用线程池
static void Main() {
Console.WriteLine("我是主线程:Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
ThreadPool.QueueUserWorkItem(Go);
Console.ReadLine();
}
public static void Go(object data) {
Console.WriteLine("我是另一个线程:Thread Id {0}",Thread.CurrentThread.ManagedThreadId);
}
传入参数
static void Main() {
new Thread(Go).Start("arg1"); // 没有匿名委托之前,我们只能这样传入一个object的参数
new Thread(delegate(){ // 有了匿名委托之后...
GoGoGo("arg1", "arg2", "arg3");
});
new Thread(() => { // 当然,还有 Lambada
GoGoGo("arg1","arg2","arg3");
}).Start();
Task.Run(() =>{ // Task能这么灵活,也是因为有了Lambda呀。
GoGoGo("arg1", "arg2", "arg3");
});
}
public static void Go(object name){
// TODO
}
public static void GoGoGo(string arg1, string arg2, string arg3){
// TODO
}
返回值
Thead是不能返回值的,但是作为更高级的Task当然要弥补一下这个功能
static void Main() {
// GetDayOfThisWeek 运行在另外一个线程中
var dayName = Task.Run<string>(() => { return GetDayOfThisWeek(); });
Console.WriteLine("今天是:{0}",dayName.Result);
}
共享数据以及线程安全
线程直接可以通过静态变量来共享数据
private static bool _isDone = false;
private static object _lock = new object();
static void Main(){
new Thread(Done).Start();
new Thread(Done).Start();
Console.ReadLine();
}
static void Done(){
lock (_lock){
if (!_isDone){
Console.WriteLine("Done"); // 猜猜这里面会被执行几次?
_isDone = true;
}
}
}
lock的资源没有释放之前其他线程是无法访问其中的代码块的,_lock 是静态的,所以,当一个线程进入后,其他的线程都需要等待
上述例子通过了静态变量实现了2个线程之间的数据共享
Semaphore
它可以控制对某一段代码或者对某个资源访问的线程的数量,超过这个数量之后,其它的线程就得等待,只有等现在有线程释放了之后,下面的线程才能访问。这个跟锁有相似的功能,只不过不是独占的,它允许一定数量的线程同时访问。
static SemaphoreSlim _sem = new SemaphoreSlim(3); // 我们限制能同时访问的线程数量是3
static void Main(){
for (int i = 1; i <= 5; i++) new Thread(Enter).Start(i);
Console.ReadLine();
}
static void Enter(object id){
Console.WriteLine(id + " 开始排队...");
_sem.Wait();
Console.WriteLine(id + " 开始执行!");
Thread.Sleep(1000 * (int)id);
Console.WriteLine(id + " 执行完毕,离开!");
_sem.Release();
}
异常处理
在使用Thread时,子线程中的异常,主线程是捕获不到的,但是Task是可以的
public static void Main(){
try{
var task = Task.Run(() => { Go(); });
task.Wait(); // 在调用了这句话之后,主线程才能捕获task里面的异常
// 对于有返回值的Task, 我们接收了它的返回值就不需要再调用Wait方法了
// GetName 里面的异常我们也可以捕获到
var task2 = Task.Run(() => { return GetName(); });
var name = task2.Result;
}
catch (Exception ex){
Console.WriteLine("Exception!");
}
}
static void Go() { throw null; }
static string GetName() { throw null; }