惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
Simon Willison's Weblog
Simon Willison's Weblog
H
Help Net Security
F
Fortinet All Blogs
H
Heimdal Security Blog
S
Schneier on Security
L
LangChain Blog
博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
NISL@THU
NISL@THU
P
Palo Alto Networks Blog
J
Java Code Geeks
博客园 - 【当耐特】
The Last Watchdog
The Last Watchdog
W
WeLiveSecurity
www.infosecurity-magazine.com
www.infosecurity-magazine.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
Vulnerabilities – Threatpost
I
InfoQ
Recorded Future
Recorded Future
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
C
CERT Recently Published Vulnerability Notes
T
Tenable Blog
腾讯CDC
C
Check Point Blog
量子位
M
MIT News - Artificial intelligence
GbyAI
GbyAI
罗磊的独立博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
B
Blog
小众软件
小众软件
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
CXSECURITY Database RSS Feed - CXSecurity.com
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed
P
Privacy & Cybersecurity Law Blog
V2EX - 技术
V2EX - 技术
T
Threatpost
Engineering at Meta
Engineering at Meta
Attack and Defense Labs
Attack and Defense Labs
T
Tailwind CSS Blog
S
Securelist
The Cloudflare Blog
博客园 - 叶小钗
L
LINUX DO - 最新话题
T
Troy Hunt's Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
爱范儿
爱范儿

博客园 - Henry Cui

招聘高级.Net工程师 Node.js Web开发(二)认识Express(上) Node.js Web开发(一)从零开始 Html5 Step by Step(二) 本地存储 HTML 5 Step by Step(一) 拖放API Git学习笔记(一)初识Git DDD中的Specification模式 Silverlight 4之旅(三)数据绑定(中) Silverlight 4之旅(二)数据绑定(上) Silverlight 4之旅(一) .Net 4.0 Parallel 编程之旅 .Net 4.0 Parallel 编程(九)Task中的数据共享(下) .Net 4.0 Parallel编程(八)Task中的数据共享(中) .Net 4.0 Parallel 编程(七)Task中的数据共享(上) .Net 4.0 Parallel 编程(五)Task(中) .Net 4.0 Parallel 编程(四) Task(上) Entity Framework Code-First(下) Entity Framework Code-First(上) Entity Framework POCO T4模板使用
.Net 4.0 Parallel 编程(六)Task(下)
Henry Cui · 2011-03-27 · via 博客园 - Henry Cui

在之前的文章中已经介绍过了Task的基本使用了,以及如何取消任务、任务继续等功能。本篇Post主要就Task的异常处理以及Wait功能进行些介绍。

等待时间执行

在TPL中我们可以通过三种方式进行等待,一是通过CancellTaken的WaitHanle进行等待、第二种则是通过传统的Tread.Sleep方法、第三种则通过Thread.SpainWait方法。

CancellToken方式:

static void Main(string[] args)
{
    var cancell = new CancellationTokenSource();
    var token = cancell.Token;
    var task = new Task(() =>
    {
        for (var i = 0; i < 100000; i++)
        {
            var cancelled = token.WaitHandle.WaitOne(10000);
            Console.WriteLine(" {0}. Cancelled? {1}",
                        i, cancelled);
            if (cancelled)
            {
                throw new OperationCanceledException(token);
            }
        }
    }, token);
    task.Start();
    Console.WriteLine("Press enter to cancel token.");
    Console.ReadLine();
    cancell.Cancel();
    Console.WriteLine("Main method complete. Press enter to finish.");
    Console.ReadLine();
}

每次我们等待十秒钟之后,在进行下次输出。

image

上面的功能如果我们要是通过Tread.Sleep方式实现:

var task = new Task(() =>
{
    for (var i = 0; i < 100000; i++)
    {
        Thread.Sleep(10000);
        var cancelled = token.IsCancellationRequested;
        Console.WriteLine(" {0}. Cancelled? {1}",
                    i, cancelled);
        if (cancelled)
        {
            throw new OperationCanceledException(token);
        }
    }
}, token);

Thread.SpainWait则跟上面的种方式完全不同,上面的两种方式都是会在线程调度程序不考虑改线程,直等到运行结束。而Thread.SpainWait的作用实质上会将处理器置于十分紧密的循环中,主要的作用是来实现同步锁的作用。并不常用,大部分情况下我们可以通过Lock的方式来实现。

等待任务执行

在很多时候我们也许需要等待同时开启的几个线程完成之后再来做其他事,在TPL中提供了几种方式来等待任务执行。Task.Wait等待单个任务完成;Task.WaitAll等待所有的Task完成、TaskAny等在其中的任何一个或则多个任务完成。

Task.Wait:

Task.Wait共有5个重载:Wait()、Wait(CancellToken)、Wait(Int32)、Wait(TimeSpan)、Wait(TimeSpan、CancellToken)。各个重载方法的含义:

1)Wait():等待整个任务完成或则取消或则出现异常;

2)Wait(CancellToken):等待任务直到CancellToken调用取消或则完成,或则出现异常;

3)Wait(Int32):等待任务,未完成则到指定的时间;

4)Wait(TimeSpan):同上;

5)Wait(TimeSpan、CancellToken):等待任务到指定时间,或则CancellToken调用取消或则任务完成。

看个示例:

static void Main(string[] args)
{
    var tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    Task task = createTask(token,6);
    task.Start();

    Console.WriteLine("Wait() complete.");
    task.Wait();
    Console.WriteLine("Task Completed.");
    task = createTask(token,3);
    task.Start();
    Console.WriteLine("Wait(2) secs for task to complete.");
    bool completed = task.Wait(2000);
    Console.WriteLine("Wait ended - task completed: {0}", completed);
    task = createTask(token,4);
    task.Start();
    Console.WriteLine("Wait(2,token) for task to complete.");
    completed = task.Wait(2000, token);
    Console.WriteLine("Wait ended - task completed: {0} task cancelled {1}",
    completed, task.IsCanceled);
    Console.WriteLine("Main method complete. Press enter to finish.");
    Console.ReadLine();
}
static Task createTask(CancellationToken token,int loop)
{
    return new Task(() =>
    {
        for (int i = 0; i < loop; i++)
        {
            token.ThrowIfCancellationRequested();
            Console.WriteLine("Task - Int value {0}", i);
            token.WaitHandle.WaitOne(1000);
        }
    }, token);
}

循环都会等待1秒钟,这样我们可以看看Wait(2000)的效果,看看运行后的效果:

image

Task.WaitAll

WaitAll方法是等待所有的任务完成,也有5个重载,我们来看示例:

static void Main(string[] args)
{
    var tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    var task1 = createTask(token,2);
    var task2 = createTask(token, 5);
    task1.Start();
    task2.Start();
    Console.WriteLine("Waiting for tasks to complete.");
    Task.WaitAll(task1, task2);
    Console.WriteLine("Tasks Completed.");
    Console.ReadKey();           
}

image

其中WaitAll,也可以传递时间以及Token参数,进行等待时间以及取消Token的控制。

Task.WaitAny

waitany是等待任何一个任务完成,完成之后返回其完成的任务的Index:

static void Main(string[] args)
{
    var tokenSource = new CancellationTokenSource();
    CancellationToken token = tokenSource.Token;
    var task1 = createTask(token,2);
    var task2 = createTask(token, 5);
    task1.Start();
    task2.Start();
    Console.WriteLine("Waiting for tasks to complete.");
    var index = Task.WaitAny(task1, task2);
    Console.WriteLine("Tasks Completed.Index is {0}",index);
    Console.ReadKey();           
}

image

异常处理

任何应用程序都需要有异常处理机制,在TPL中提供了一套可靠的异常处理机制给我们使用。首先我啊们要知道在哪些环节会触发异常,在TPL中,异常的触发器主要是这几个:

Task.Wait(), Task.WaitAll(), Task,WaitAny(),Task.Result。而在TPL出现的异常都会以AggregateException的示例抛出,我们在进行基本的异常处理时,可以通过查看AggregateException的InnerExceptions来进行内部异常的捕获:

static void Main(string[] args)
{
    var tokenSource = new CancellationTokenSource();
    var token = tokenSource.Token;
    var task1 = new Task(() =>
    {
        throw new NullReferenceException() { Source="task1"};
    });
    var task2 = new Task(() =>
    {
        throw new ArgumentNullException("a", "a para can not be null") { Source="task2"};
    });
    var task3 = new Task(() =>
    {
        throw new DivideByZeroException("Can not be zero") { Source = "task3" };
    });
    var task4 = new Task(() =>
    {
        Console.WriteLine("Task4 .");
    });
    task1.Start(); task2.Start(); task3.Start(); task4.Start();
    try
    {
        Task.WaitAll(task1, task2, task3, task4);
    }
    catch(AggregateException ex)
    {
        foreach (Exception inner in ex.InnerExceptions)
        {
            Console.WriteLine("Exception type {0} from {1}",
            inner.GetType(), inner.Source);
        }
    }
    Console.ReadLine();
}

上面的例子中通过便利AggregateException的InnerException进行详细异常的处理,效果:

image

同时,我们还可以通过Task的几个属性来判断Task的状态,如:IsCompleted, IsFaulted, IsCancelled,Exception。另外,AggregateException中还提供了

Handle方法来给我们方法来给我们处理每个内部 异常,每个异常发生时都会调用Handle传入的delegate ,同时我们需要通过返回True,False来告诉异常是否已经被处理,比如对于OperationCanceledException我们知道是取消了Task,是肯定可以处理的:

    try
    {
        Task.WaitAll(task1, task2, task3, task4);
    }
    catch(AggregateException ex)
    {
        ex.Handle((e) =>
        {
            if (e is OperationCanceledException)
            {
                return true;
            }
            else
            {
                return false;
            }
        });
        
    }
    

总结

本篇Post中,我们主要学习了Wait 、Wait for task以及异常的处理,希望对您有用。(PS:好长时间不写Blog了,之间间隔了好几个月。)

alt