c#中task与thread区别及其使用的方法示例
1.什么是thread
当我们提及多线程的时候会想到thread和threadpool,这都是异步操作,threadpool其实就是thread的集合,具有很多优势,不过在任务多的时候全局队列会存在竞争而消耗资源。thread默认为前台线程,主程序必须等线程跑完才会关闭,而threadpool相反。
总结:threadpool确实比thread性能优,但是两者都没有很好的api区控制,如果线程执行无响应就只能等待结束,从而诞生了task任务。
2.什么是task
task简单地看就是任务,那和thread有什么区别呢?task的背后的实现也是使用了线程池线程,但它的性能优于threadpoll,因为它使用的不是线程池的全局队列,而是使用的本地队列,使线程之间的资源竞争减少。同时task提供了丰富的api来管理线程、控制。但是相对前面的两种耗内存,task依赖于cpu对于多核的cpu性能远超前两者,单核的cpu三者的性能没什么差别。
3.创建一个task任务有两种模式:
1.使用factory创建会直接执行,使用new创建不会执行,必须等到start启动之后才执行。
public void test() { var testtask = new task(() => { console.writeline("task start"); }); testtask.start(); var factoryteak = task.factory.startnew(() => { console.writeline("factory task start"); }); }
2.我们来看看task的生命周期
var testtask = new task(() => { console.writeline("task start"); system.threading.thread.sleep(2000); }); console.writeline(testtask.status); testtask.start(); console.writeline(testtask.status); console.writeline(testtask.status); testtask.wait(); console.writeline(testtask.status); console.writeline(testtask.status);
输出结果:
created
task start
running
running
rantocompletion
rantocompletion
可以看出task确实是异步执行,并且wait很好地控制了task。
3.下面演示几个控制task的方法
var testtask = new task(() => { console.writeline("task start"); system.threading.thread.sleep(2000); }); testtask.start(); testtask.wait();
var testtask = new task(() => { console.writeline("task start"); system.threading.thread.sleep(2000); }); testtask.start(); var factoryteak = task.factory.startnew(() => { console.writeline("factory task start"); }); task.waitall(testtask, factoryteak); console.writeline("end");
var testtask = new task(() => { console.writeline("task start"); system.threading.thread.sleep(2000); }); testtask.start(); var factoryteak = task.factory.startnew(() => { console.writeline("factory task start"); }); task.waitany(testtask, factoryteak); console.writeline("end");
通过wait()对单个task进行等待,task.waitall()对多个task进行等待,waitany()执行任意一个task就往下继续执行。
4.task的回调执行
var testtask = new task(() => { console.writeline("task start"); system.threading.thread.sleep(2000); }); testtask.start(); var resulttest = testtask.continuewith<string>((task) => { console.writeline("testtask end"); return "end"; }); console.writeline(resulttest.result);
5.task的取消
首先创建一个取消task的令牌的实例,在不启动task直接取消:
var tokensource = new cancellationtokensource();//创建取消task实例 var testtask = new task(() => { for (int i = 0; i < 6; i++) { system.threading.thread.sleep(1000); } },tokensource.token); console.writeline(testtask.status); tokensource.token.register(()=> { console.writeline("task is to cancel"); }); tokensource.cancel(); console.writeline(testtask.status);
输出结果:
created
task is to cancel
canceled
如果task启动了真的取消了task?
var tokensource = new cancellationtokensource();//创建取消task实例 var testtask = new task(() => { for (int i = 0; i <6; i++) { system.threading.thread.sleep(1000); } },tokensource.token); console.writeline(testtask.status); testtask.start(); console.writeline(testtask.status); tokensource.token.register(()=> { console.writeline("task is to cancel"); }); tokensource.cancel(); console.writeline(testtask.status); for (int i = 0; i < 10; i++) { system.threading.thread.sleep(1000); console.writeline(testtask.status); }
输出结果:
created
waitingtorun
task is to cancel
running
running
running
running
running
running
rantocompletion
rantocompletion
rantocompletion
rantocompletion
rantocompletion
可以看出其实并没有取消task,此时task还在继续跑。
6.task的嵌套
var parenttask = new task(()=> { var childtask = new task(() =>{ system.threading.thread.sleep(2000); console.writeline("childtask to start"); }); childtask.start(); console.writeline("parenttask to start"); }); parenttask.start(); parenttask.wait(); console.writeline("end");
此时为普通关联,父task和子task没影响
var parenttask = new task(()=> { var childtask = new task(() =>{ system.threading.thread.sleep(2000); console.writeline("childtask to start"); }, taskcreationoptions.attachedtoparent); childtask.start(); console.writeline("parenttask to start"); } ); parenttask.start(); parenttask.wait(); console.writeline("end");
此时为父task和子task关联,wait会一直等待父子task执行完。
6.task死锁的问题
我们可以设置最大等待时间,如果超过了等待时间,就不再等待,下面我们来修改代码,设置最大等待时间为5秒(项目中可以根据实际情况设置),如果超过5秒就输出哪个任务出错了
7.对spinlock的使用
举例来说parallel.for和parallel.foreach是线程不安全的,有可能达不到你的预期,此时就需要加锁来解决此问题,我们可以加lock和spinlock(自旋锁)来解决
spinlock slock = new spinlock(false); var testlock= new object(); long sum1 = 0; long sum2 = 0; long sum3 = 0; parallel.for(0, 100000, i => { sum1 += i; }); parallel.for(0, 100000, i => { bool locktaken = false; try { slock.enter(ref locktaken); sum2 += i; } finally { if (locktaken) slock.exit(false); } }); parallel.for(0, 100000, i => { lock(testlock) { sum3 += i; }; }); console.writeline("num1的值为:{0}", sum1); console.writeline("num2的值为:{0}", sum2); console.writeline("num3的值为:{0}", sum3);
输出结果:
num1的值为:1660913202
num2的值为:4999950000
num3的值为:4999950000
num1的值为:2754493646
num2的值为:4999950000
num3的值为:4999950000
num1的值为:4999950000
num2的值为:4999950000
num3的值为:4999950000
最后看看threadpoll和task的结构图:
threadpool:
task:
上一篇: c#中token的使用方法实例