程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET並行(多核)編程系列之五 Task執行和異常處理

.NET並行(多核)編程系列之五 Task執行和異常處理

編輯:關於.NET

前言:本篇主要講述等待task執行完成。

本篇的議題如下:

1. 等待Task執行完成

2. Task中的異常處理

首先注意一點:這裡提到的"等待"和之前文章提到的"休眠"意思是不一樣的:

等待:在等待一個task的時候,這個task還是在運行之中的,"等待"相當於在監聽運行的task的執行情況。

休眠:讓tasku不運行。

在上篇文章中介紹了如果從Task中獲取執行後的結果:在Task執行完成之後調用Task.Result獲取。其實也可以用其他的方法等待Task執行完成而不獲取結果,這是很有用的:如果你想等待一個task完成之後再去做其他的事情。而且我們還可以等待一個task執行完成,或者等待所有的task執行完成,或者等待很多task中的一個執行完成。因為Task是由內部的Scheduler管理的,調用wait方法,其實就是我們在監控task的執行,看看這個task是否執行完了,如果完成,那麼wanit方法就返回true,反之。

1. 等待Task執行完成

1.1等待單獨的一個Task執行完成

我們可以用Wait()方法來一直等待一個Task執行完成。當task執行完成,或者被cancel,或者拋出異常,這個方法才會返回。可以使用Wait()方法的不同重載。舉個例子:

static void Main(string[] args)
{
// create the cancellation token source
CancellationTokenSource tokenSource = new CancellationTokenSource();
// create the cancellation token
CancellationToken token = tokenSource.Token;
// create and start the first task, which we will let run fully
Task task = createTask(token);
task.Start();
// wait for the task
Console.WriteLine("Waiting for task to complete.");
task.Wait();
Console.WriteLine("Task Completed.");
// create and start another task
task = createTask(token);
task.Start();
Console.WriteLine("Waiting 2 secs for task to complete.");
bool completed = task.Wait(2000);
Console.WriteLine("Wait ended - task completed: {0}", completed);
// create and start another task
task = createTask(token);
task.Start();
Console.WriteLine("Waiting 2 secs for task to complete.");
completed = task.Wait(2000, token);
Console.WriteLine("Wait ended - task completed: {0} task cancelled {1}",
completed, task.IsCanceled);
// wait for input before exiting
Console.WriteLine("Main method complete. Press enter to finish.");
Console.ReadLine();
}
static Task createTask(CancellationToken token)
{
return new Task(() =>
{
for (int i = 0; i < 5; i++)
{
// check for task cancellation
token.ThrowIfCancellationRequested();
// print out a message
Console.WriteLine("Task - Int value {0}", i);
// put the task to sleep for 1 second
token.WaitHandle.WaitOne(1000);
}
}, token);
}

從上面的例子可以看出,wait方法子task執行完成之後會返回true。

注意:當在執行的task內部拋出了異常之後,這個異常在調用wait方法時會被再次拋出。後面再"異常處理篇"會講述。

1.2.等待多個task

我們也可以用WaitAll()方法來一直到等待多個task執行完成。只有當所有的task執行完成,或者被cancel,或者拋出異常,這個方法才會返回。WiatAll()方法和Wait()方法一樣有一些重載。

注意:如果在等在的多個task之中,有一個task拋出了異常,那麼調用WaitAll()方法時就會拋出異常。

static void Main(string[] args)
{
// create the cancellation token source
CancellationTokenSource tokenSource = new CancellationTokenSource();
// create the cancellation token
CancellationToken token = tokenSource.Token;
// create the tasks
Task task1 = new Task(() =>
{
for (int i = 0; i < 5; i++)
{
// check for task cancellation
token.ThrowIfCancellationRequested();
// print out a message
Console.WriteLine("Task 1 - Int value {0}", i);
// put the task to sleep for 1 second
token.WaitHandle.WaitOne(1000);
}
Console.WriteLine("Task 1 complete");
}, token);
Task task2 = new Task(() =>
{
Console.WriteLine("Task 2 complete");
}, token);
// start the tasks
task1.Start();
task2.Start();
// wait for the tasks
Console.WriteLine("Waiting for tasks to complete.");
Task.WaitAll(task1, task2);
Console.WriteLine("Tasks Completed.");
// wait for input before exiting
Console.WriteLine("Main method complete. Press enter to finish.");
Console.ReadLine();
}

在上面的例子中,首先創建了兩個task,注意我們創建的是可以被cancel的task,因為使用CancellationToken。而且在第一個task中還是用waitOne()休眠方法,其實目的很簡單:使得這個task的運行時間長一點而已。之後我們就調用了WaitAll()方法,這個方法一直到兩個task執行完成之後才會返回的。

1.3.等待多個task中的一個task執行完成

可以用WaitAny()方法來等待多個task中的一個task執行完成。通俗的講就是:有很多的task在運行,調用了WaitAny()方法之後,只要那些運行的task其中有一個運行完成了,那麼WaitAny()就返回了。

static void Main(string[] args)
{
// create the cancellation token source
CancellationTokenSource tokenSource = new CancellationTokenSource();
// create the cancellation token
CancellationToken token = tokenSource.Token;
// create the tasks
Task task1 = new Task(() =>
{
for (int i = 0; i < 5; i++)
{
// check for task cancellation
token.ThrowIfCancellationRequested();
// print out a message
Console.WriteLine("Task 1 - Int value {0}", i);
// put the task to sleep for 1 second
token.WaitHandle.WaitOne(1000);
}
Console.WriteLine("Task 1 complete");
}, token);
Task task2 = new Task(() =>
{
Console.WriteLine("Task 2 complete");
}, token);
// start the tasks
task1.Start();
task2.Start();
// wait for the tasks
Console.WriteLine("Waiting for tasks to complete.");
int taskIndex = Task.WaitAny(task1, task2);
Console.WriteLine("Task Completed. Index: {0}", taskIndex);
// wait for input before exiting
Console.WriteLine("Main method complete. Press enter to finish.");
Console.ReadLine();
}

2. Task中的異常處理

在並行編程(TPL)中另外一個已經標准化了的操作就是"異常處理"。而且在並行編程中異常處理顯得尤為重要,因為並行編程時與系統中的線程相關的,出了異常,你開發的程序就很有可能崩潰。

下面就詳細介紹TPL中異常處理操作。

a.處理基本的異常。

在操作task的時候,只要出現了異常,.NET Framework就會把這些異常記錄下來。例如在執行Task.Wait(),Task.WaitAll(),Task.WaitAny(),Task.Result.不管那裡出現了異常,最後拋出的就是一個System.AggregateException.

System.AggregateException時用來包裝一個或者多個異常的,這個類時很有用的,特別是在調用Task.WaitAll()方法時。因為在Task.WaitAll()是等待多個task執行完成,如果有任意task執行出了異常,那麼這個異常就會被記錄在System.AggregateException中,不同的task可能拋出的異常不同,但是這些異常都會被記錄下來。

下面就是給出一個例子:在例子中,創建了兩個task,它們都拋出異常。然後主線程開始運行task,並且調用WaitAll()方法,然後就捕獲拋出的System.AggregateException,顯示詳細信息。

static void Main(string[] args)
{
// create the tasks
Task task1 = new Task(() =>
{
ArgumentOutOfRangeException exception = new ArgumentOutOfRangeException();
exception.Source = "task1";
throw exception;
});
Task task2 = new Task(() =>
{
throw new NullReferenceException();
});
Task task3 = new Task(() =>
{
Console.WriteLine("Hello from Task 3");
});
// start the tasks
task1.Start(); task2.Start(); task3.Start();
// wait for all of the tasks to complete
// and wrap the method in a try...catch block
try
{
Task.WaitAll(task1, task2, task3);
}
catch (AggregateException ex)
{
// enumerate the exceptions that have been aggregated
foreach (Exception inner in ex.InnerExceptions)
{
Console.WriteLine("Exception type {0} from {1}",
inner.GetType(), inner.Source);
}
}
// wait for input before exiting
Console.WriteLine("Main method complete. Press enter to finish.");
Console.ReadLine();
}

從上面的例子可以看出,為了獲得被包裝起來的異常,需要調用System.AggregateException的InnerExceptions屬性,這個屬性返回一個異常的集合,然後就可以遍歷這個集合。

而且從上面的例子可以看到:Exeception.Source屬性被用來指明task1的異常時ArgumentOutRangeException.

b.使用迭代的異常處理Handler

一般情況下,我們需要區分哪些異常需要處理,而哪些異常需要繼續往上傳遞。AggregateException類提供了一個Handle()方法,我們可以用這個方法來處理

AggregateException中的每一個異常。在這個Handle()方法中,返回true就表明,這個異常我們已經處理了,不用拋出,反之。

在下面的例子中,拋出了一個OperationCancelException,在之前的task的取消一文中,已經提到過:當在task中拋出這個異常的時候,實際上就是這個task發送了取消的請求。下面的代碼中,描述了如果在AggregateException.Handle()中處理不同的異常。

static void Main(string[] args)
{
// create the cancellation token source and the token
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
// create a task that waits on the cancellation token
Task task1 = new Task(() =>
{
// wait forever or until the token is cancelled
token.WaitHandle.WaitOne(-1);
// throw an exception to acknowledge the cancellation
throw new OperationCanceledException(token);
}, token);
// create a task that throws an exception
Task task2 = new Task(() =>
{
throw new NullReferenceException();
});
// start the tasks
task1.Start(); task2.Start();
// cancel the token
tokenSource.Cancel();
// wait on the tasks and catch any exceptions
try
{
Task.WaitAll(task1, task2);
}
catch (AggregateException ex)
{
// iterate through the inner exceptions using
// the handle method
ex.Handle((inner) =>
{
if (inner is OperationCanceledException)
{
// ...handle task cancellation...
return true;
}
else
{
// this is an exception we don't know how
// to handle, so return false
return false;
}
});
}
// wait for input before exiting
Console.WriteLine("Main method complete. Press enter to finish.");
Console.ReadLine();
}
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved