.NET 4中的多线程编程之一:使用Task(转载)
2017-04-25 17:06
267 查看
原文地址:http://www.cnblogs.com/yinzixin/archive/2011/11/04/2235233.html
.NET4为了简化多线程编程,提供了System.Threading.Tasks命名空间中的类来帮助开发者进行多线程编程,其中,Task类用来表示一个线程。最简单的Task类接受一个Action委托作为要执行的方法,调用Start方法开始在另一个线程中运行。例如:
MainThread
Writefromanotherthread
也可以使用Task.Factory.StartNew方法,这个方法会构造一个Task并且立刻开始运行,相当于将Task的构造函数和Start方法连在一起执行。
Task类还有一个构造函数可以接受Action<object>委托,用来向Action委托传递参数:
输出的结果类似于:
ThreadNo2
ThreadNo1
ThreadNo3
ThreadNo0
ThreadNo4
可以使用Task<T>类来获得返回值,T是返回值的类型,例如:
I'mcomputing
49995000
在访问t.Result的时候,.net会保证此时Task的代码已经执行完毕,Result已经获得,否则该线程会阻塞,直到Result计算完毕。
Task库提供了一种主动终止线程的方法,先创建一个CancellationTokenSource,将其Token属性通过Task构造函数传进去,在Task内部轮询token的IsCancellationReqeusted属性,如果检测到为true,则主动终止线程。在父线程内调用tokenSource的Cancel方法,可以终止线程。注意,这是线程主动终止自己的方法,必须在Task内的代码自己终止,.NET不会强行终止task线程,即使父线程调用了tokenSource的Cancel方法。
例如下面的代码,如果在else语句块内没有break语句,子线程是不会终止的。
还可以通过CancellationToken的Register方法给token注册一个委托,当调用tokenSource的Cancel方法的时候,这个委托会被执行。这个委托是在调用Cancel方法的线程中执行的。
执行结果为:
Task1Starts...
Task2Starts...
Task3iswaitingTask1for1second...
T1isnotfinished
Task1Ends
Task2Ends
要等到多个线程都执行完可以使用Task.WaitAll方法,
还有一个Task.WaitAny,可以等待一组线程中的任何一个方法执行完毕,用法类似。
下面介绍Task中的异常处理,通常情况下,线程委托中的异常会导致线程终止,但异常并不会被抛出,例如:
输出的是:
MainEnds
当调用Wait,WaitAll,WaitAny,Task.Result的时候,会抛出AggerateException,在AggerateExcepiton中可以处理所有线程抛出的异常:
有时候需要区分对待某些异常,一些异常直接处理掉,一些异常需要再次抛出,AggregateException提供一个Handle方法,接收一个Func<Exception,bool>委托作为参数,如果不需要再次抛出则返回true,否则返回false。
.NET4为了简化多线程编程,提供了System.Threading.Tasks命名空间中的类来帮助开发者进行多线程编程,其中,Task类用来表示一个线程。最简单的Task类接受一个Action委托作为要执行的方法,调用Start方法开始在另一个线程中运行。例如:
usingSystem; usingSystem.Threading.Tasks; namespaceTaskParallel { classProgram { staticvoidMain(string[]args) { Tasktask=newTask(()=>Console.WriteLine("Writefromanotherthread")); task.Start(); Console.WriteLine("MainThread"); Console.ReadLine(); } } } 两句输出的顺序是不一定的,但是很有可能是:
MainThread
Writefromanotherthread
也可以使用Task.Factory.StartNew方法,这个方法会构造一个Task并且立刻开始运行,相当于将Task的构造函数和Start方法连在一起执行。
Task类还有一个构造函数可以接受Action<object>委托,用来向Action委托传递参数:
staticvoidMain(string[]args) { for(inti=0;i<5;i++) { Taskt=newTask(obj=>Console.WriteLine("ThreadNo"+obj),i); t.Start(); } Console.ReadLine(); }
输出的结果类似于:
ThreadNo2
ThreadNo1
ThreadNo3
ThreadNo0
ThreadNo4
可以使用Task<T>类来获得返回值,T是返回值的类型,例如:
staticvoidMain(string[]args) { Task<int>t=newTask<int>(()=> { ints=0; for(inti=0;i<10000;i++) s+=i; returns; }); t.Start(); Console.WriteLine("I'mcomputing"); Console.WriteLine(t.Result); Console.ReadLine(); } 结果为:
I'mcomputing
49995000
在访问t.Result的时候,.net会保证此时Task的代码已经执行完毕,Result已经获得,否则该线程会阻塞,直到Result计算完毕。
Task库提供了一种主动终止线程的方法,先创建一个CancellationTokenSource,将其Token属性通过Task构造函数传进去,在Task内部轮询token的IsCancellationReqeusted属性,如果检测到为true,则主动终止线程。在父线程内调用tokenSource的Cancel方法,可以终止线程。注意,这是线程主动终止自己的方法,必须在Task内的代码自己终止,.NET不会强行终止task线程,即使父线程调用了tokenSource的Cancel方法。
例如下面的代码,如果在else语句块内没有break语句,子线程是不会终止的。
usingSystem; usingSystem.Threading; usingSystem.Threading.Tasks; namespaceTaskParallel { classProgram { staticvoidMain(string[]args) { CancellationTokenSourcetks=newCancellationTokenSource(); CancellationTokentoken=tks.Token; longi=0; Tasktask=newTask(()=> { while(true) { if(!token.IsCancellationRequested) i++; else { Console.WriteLine("Taskiscanceled,itlooped"+i+"times"); break; } } },token); task.Start(); Console.WriteLine("PressEntertoCanceltask"); Console.ReadLine(); tks.Cancel(); Console.ReadLine(); } } }
还可以通过CancellationToken的Register方法给token注册一个委托,当调用tokenSource的Cancel方法的时候,这个委托会被执行。这个委托是在调用Cancel方法的线程中执行的。
token.Register(()=>{Console.WriteLine("DelegateInvoked");});
要挂起当前线程,等待一个线程执行完成,可以使用执行线程的Wait()方法,Wait方法有一些重载方法,可以指定等待的时间等,例如:
staticvoidMain(string[]args)
{
Taskt1=newTask(()=>
{
Console.WriteLine("Task1Starts...");
Thread.Sleep(3000);
Console.WriteLine("Task1Ends");
});
t1.Start();
Taskt2=newTask(()=>
{
Console.WriteLine("Task2Starts...");
t1.Wait();
Console.WriteLine("Task2Ends");
});
Taskt3=newTask(()=>
{
Console.WriteLine("Task3iswaitingTask1for1second...");
boolfinished=t1.Wait(1000);
if(finished)
Console.WriteLine("T1isfinished");
else
Console.WriteLine("T1isnotfinished");
});
t2.Start();
t3.Start();
Console.ReadLine();
}
执行结果为:
Task1Starts...
Task2Starts...
Task3iswaitingTask1for1second...
T1isnotfinished
Task1Ends
Task2Ends
要等到多个线程都执行完可以使用Task.WaitAll方法,
Taskt4=newTask(()=>
{
Console.WriteLine("Waitingforall");
Task.WaitAll(t1,t2,t3);
Console.WriteLine("Task4Ends");
});
t4.Start();
还有一个Task.WaitAny,可以等待一组线程中的任何一个方法执行完毕,用法类似。
下面介绍Task中的异常处理,通常情况下,线程委托中的异常会导致线程终止,但异常并不会被抛出,例如:
Taskt=newTask(()=>
{
thrownewException();
Console.WriteLine("ThreadEnds");
});
t.Start();
Console.WriteLine("MainEnds");
输出的是:
MainEnds
当调用Wait,WaitAll,WaitAny,Task.Result的时候,会抛出
staticvoidMain(string[]args)
{
Taskt1=newTask(()=>
{
thrownewException();
Console.WriteLine("T1Ends");
});
Taskt2=newTask(()=>
{
thrownewArgumentException();
Console.WriteLine("T2Ends");
});
t1.Start();
t2.Start();
try
{
Task.WaitAll(t1,t2);
}
catch(AggregateExceptionex)
{
foreach(varinnerinex.InnerExceptions)
{
Console.WriteLine(inner.GetType()+""+inner.Source);
}
}
Console.WriteLine("MainEnds");
}
}
有时候需要区分对待某些异常,一些异常直接处理掉,一些异常需要再次抛出,AggregateException提供一个Handle方法,接收一个Func<Exception,bool>委托作为参数,如果不需要再次抛出则返回true,否则返回false。
try
{
Task.WaitAll(t1,t2);
}
catch(AggregateExceptionex)
{
foreach(varinnerinex.InnerExceptions)
{
Console.WriteLine(inner.GetType()+""+inner.Source);
}
ex.Handle((e)=>
{
if(eisArgumentException)
{
Console.WriteLine("ArgumentExceptioniscaptured");
returntrue;
}
else
returnfalse;
});
}
这样就会有未处理的异常被抛出,抛出的仍然是AggregateException.
相关文章推荐
- .NET 4中的多线程编程之一:使用Task
- .NET下的多线程编程—2-Thread的使用
- (转自博客园-雲霏霏)5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 讲述.NET下的多线程编程在多方面使用
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 使用NUnit在.Net编程中进行单元测试(转载)
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- [转载]使用NUnit在.Net编程中进行单元测试
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- .Net多线程编程—使用Visual Studio 2012进行调试
- 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
- 使用NUnit在.Net编程中进行单元测试【转载】
- .NET使用Task动态创建多任务多线程并行程序计算Redis集群keys计算
- Office with .Net (二) ――― 使用.Net访问Office编程接口
- 如何使用.NET配置文件(转载)
- Office with .Net (二) ――― 使用.Net访问Office编程接口
- 在Javascript中使用面向对象的编程(转载)