C#深度学习のTask(基于任务的异步模型)

一、Task关键字解释

Task 类的表示的单个操作不会返回一个值,通常以异步方式执行。 Task 对象是一种的中心思想 基于任务的异步编程模式 首次引入.NET Framework 4 中。 因为由执行工作 Task 对象通常上异步执行一个线程池线程而不是以同步方式在主应用程序线程中,您可以使用 Status 属性,以及 IsCanceled, , IsCompleted, ,和 IsFaulted 属性,以此来确定任务的状态。 大多数情况下,lambda 表达式用于指定的任务是执行的工作。可以说在某种程度上类似于线程池,但比线程池更容易使用。

需要用到的程序集

mscorlib(在 mscorlib.dll 中)
System.Threading.Tasks(在 System.Threading.Tasks.dll 中)

二、Task和ThreadPool

Task是在ThreadPool的基础上推出的,ThreadPool中有若干数量的线程,如果有任务需要处理时,会从线程池中获取一个空闲的线程来执行任务,任务执行完毕后线程不会销毁,而是被线程池回收以供后续任务使用。当线程池中所有的线程都在忙碌时,又有新任务要处理时,线程池才会新建一个线程来处理该任务,如果线程数量达到设置的最大值,任务会排队,等待其他任务释放线程后再执行。线程池能减少线程的创建,节省开销。

看下面代码

    class Program
{
static void Main(string[] args)
{
for (int i = ; i <= ; i++)
{
//ThreadPool执行任务
ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
{
Thread.Sleep();
Console.WriteLine($"第{obj}个执行任务");
}), i);
}
Console.ReadKey();
}
}
}

结果是:

C#深度学习のTask(基于任务的异步模型)

相对于Thread优势:

1、代码简单

2、减少不必要的创建,降低开销

自身缺点:

ThreadPool不能控制线程的执行顺序,我们也不能获取线程池内线程取消/异常/完成的通知,即我们不能有效监控和控制线程池中的线程。

三、Task的使用

1、类厂调用

          Task t=  Task.Factory.StartNew(() =>
{
for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from lambda:" + i);
}
}); t.Wait(); for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from main:"+i);
}

一般的,如果我们不需要得到lambda的返回值,也不需要控制它和主线程中的线程关系,只要求它和主线程同时高效执行Task.Factory.StartNew就好,如果需要阻塞主线程,则需要给任务一个引用t,并在需要的地方调用阻塞方法Wait,WaitAll是针对任务链

2、Task的构造函数初始化 (可以添加第二个参数附加信息)

需要调用task,开始执行任务

          Task t=  new Task(() =>
{
for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from lambda:" + i);
}
}); t.Start(); for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from main:"+i);
} t.Wait();

3、Run

          Task t=  Task.Run(() =>
{
for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from lambda:" + i);
}
}); for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from main:"+i);
} t.Wait();

四、同步执行

Task提供了  task.RunSynchronously()用于同步执行Task任务

          Task t=  new Task(() =>
{
for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from lambda:" + i);
}
}); t.RunSynchronously(); for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from main:"+i);
} t.Wait();

五、有返回值

            Func<string> func = ()=>
{
System.Threading.Thread.Sleep();
return "from func";
}; Task<string> t= new Task<string>(func); t.Start(); for (int i = ; i < ; i++)
{
System.Threading.Thread.Sleep();
Console.WriteLine("from main:"+i);
} t.Wait(); Console.WriteLine(t.Result);

调用t.result的时候,调用线程会阻塞

六、Task任务的阻塞(Wait/WaitAll/WaitAny)

 Thread的Join方法可以阻塞调用线程,但是有一些弊端:①如果我们要实现很多线程的阻塞时,每个线程都要调用一次Join方法;②如果我们想让所有的线程执行完毕(或者任一线程执行完毕)时,立即解除阻塞,使用Join方法不容易实现。Task提供了  Wait/WaitAny/WaitAll  方法,可以更方便地控制线程阻塞。

task.Wait()  表示等待task执行完毕,功能类似于thead.Join();  Task.WaitAll(Task[] tasks)  表示只有所有的task都执行完成了再解除阻塞;  Task.WaitAny(Task[] tasks) 表示只要有一个task执行完毕就解除阻塞

如果将栗子中的WaitAll换成WaitAny,那么任一task执行完毕就会解除线程阻塞,执行结果是:先打印【线程1执行完毕】,然后打印【主线程执行完毕】,最后打印【线程2执行完毕】

七、Task任务的取消

1 Thread取消任务执行

在Task前我们执行任务采用的是Thread,Thread如何取消任务,可以建立一个key

 
        static void Main(string[] args)
{
bool key= false;
int index = 0;
Thread t= new Thread(() =>
{
while (!key)
{
Thread.Sleep(1000);
Console.WriteLine("thread operating....");
}
});
t.Start();
//五秒后取消任务执行
Thread.Sleep(5000);
key= true;
Console.ReadKey();
}
 

  Task中有一个专门的类 CancellationTokenSource  来取消任务执行,还是使用上边的例子,我们修改代码如下,程序运行的效果不变。

        static void Main(string[] args)
{
CancellationTokenSource source = new CancellationTokenSource();
int index = 0;
//开启一个task执行任务
Task task= new Task(() =>
{
while (!source.IsCancellationRequested)
{
Thread.Sleep(1000);
Console.WriteLine("task operating...");
}
});
task.Start();
//五秒后取消任务执行
Thread.Sleep(5000);
//source.Cancel()方法请求取消任务,IsCancellationRequested会变成true
source.Cancel();
Console.ReadKey();
}
 

有疑问请加qq群:568055323

上一篇:Java多线程5:Synchronized锁机制


下一篇:WebGL入门教程(二)-webgl绘制三角形