Task和thread区别和使用

本文详细解析了C#中Task与Thread的区别,包括它们的底层实现、性能对比、API控制能力以及如何创建和管理Task。通过示例展示了Task的生命周期、控制方法、回调执行、取消机制、嵌套使用和死锁问题的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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((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

近日灵感飙升,突发奇想,模拟操作系统调度进程的方式写了个调度函数的类库,暂称为TaskThread,此类皆组合或继承自TTaskThread类,它们是一种奇妙的类,它们可以把一个一个的现成函数当成一个“process"强行塞入TaskThread的线程运行,用户不再需要额外初始化线程类。本函数库甚至能模拟操作系统的方式对“任务函数”进行调度运行,目前的策略支持批处理并行模式,类似OS的批处理文件运行多任务并发执行。为了便于C++用户者使用,除了对普通函数实现了“任务化”管理之外,本类库所有任务类均实现了对C++类函数作“任务化”管理的TClassTaskThread另一系列版本。 TTaskThread类库总共包括了大大小小几十个类,提供了跨平台(目前只实现的windows)的线程、互斥等基础类,主要实现功能的类是TClassTaskThread系列类,最多带9个参数的函数或类函数的任务管理,以下是类继承关系。 以下是类继承关系图 -TThreadMutex 互斥类 -TSimpleThread 基础线程类 -TClassTaskThread 运行类函数的任务类基类 -TClassNoRetNoArgTaskThread -TClassNoRet1ArgTaskThread -TClassNoRet2ArgTaskThread ...直到支持9个参数的无返回类函数任务类 -TTaskThread 运行普通函数任务类基类 -TNoRetNoArgTaskThread -TNoRet1ArgTaskThread -TNoRet2ArgTaskThread ......直到支持9个参数的无返回函数任务类 -TMutiTaskThread9BatchFacet 批处理运行适应类 -TMutiTaskThread9ParallelFacet并行运行适应类 -TClassMutiTaskThread9BatchFacet 批处理运行适应类 (类函数) -TClassMutiTaskThread9ParallelFacet并行运行适应类 (类函数) -TMutiTaskThread9 多任务管理器类 -TClassMutiTaskThread9 多任务管理器类 (类函数) -TMutiTaskThread9<TMutiTaskThread9BatchFacet> TBatchTaskThreads批处理管理器类 -TMutiTaskThread9<TMutiTaskThread9ParallelFacet> TParallelTaskThreads并行管理器类 -TClassMutiTaskThread9<TClassMutiTaskThread9BatchFacet> TClassBatchTaskThreads批处理管理器类 (类函数) -TClassMutiTaskThread9<TClassMutiTaskThread9ParallelFacet> TClassParallelTaskThreads并行管理器类 (类函数) 源代码因为大量使用模板、类函数指针等高级的C++特性,因此需要比较新版,接近C99标准的C++编译器,笔者是使用Borland C++ 6编译通过。 以下是一个管理批处理任务的使用示例: int c=0; void AsignC() { c=a+b; } int negtive(int d) { return -d; } TBatchTaskThreads tbth9; tbth9.PushBackTaskNoRet(AsignC); tbth9.PushBackTaskRet<int,int>(negtive,112); tbth9.PushBackTaskRet<int,int>(negtive,9); tbth9.Start(); tbth9.WaitForMutiTask(); ShowMessage(IntToStr(GetTickCount()-curtime)); ShowMessage(IntToStr(c)); int testi=tbth9.GetRunedRet<int>(1); ShowMessage(IntToStr(testi)); testi=tbth9.GetRunedRet<int>(2); ShowMessage(IntToStr(testi)); tbth9.ClearTask(); 可以看出,用户只需要把待批处理运行的函数按次序塞入tbth9,然后运行Start,再WaitForMutiTask,之后就可以按照需要取回函数运行结果。可见,TBatchTaskThreads 调用方式是非常简单的。 并行方式的调用方式类似,不再赘述。 以下是完整的源代码,有兴趣者可以复制并散播,但请不要去掉文件头的版权信息,请尊重作者的精神享有权。 笔者手中还有完整的C++Builder 6的DEMO演示例程,如有兴趣,请email至superyys@163.com索取,绝不吝啬。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值