多線程之任務: Task 基礎, 多任務並行執行, 並行運算(Parallel)
介紹
重新想象 Windows 8 Store Apps 之 任務
Task - 基於線程池的任務(在 System.Threading.Tasks 命名空間下)
多 Task 的並行執行
Parallel - 並行計算(在 System.Threading.Tasks 命名空間下)
示例
1、演示 Task(基於線程池的任務)的基本應用
Thread/Tasks/TaskDemo.xaml
<Page
x:Class="XamlDemo.Thread.Tasks.TaskDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlDemo.Thread.Tasks"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent">
<StackPanel Margin="120 0 0 0">
<TextBlock Name="lblMsg" FontSize="14.667" />
<Button Name="btnCreateTask" Content="執行一個沒有返回值的任務" Click="btnCreateTask_Click_1" Margin="0 10 0 0" />
<Button Name="btnCancelTask" Content="取消“執行一個沒有返回值的任務”" Click="btnCancelTask_Click_1" Margin="0 10 0 0" />
<Button Name="btnCreateTaskWithReturn" Content="執行一個帶返回值的任務" Click="btnCreateTaskWithReturn_Click_1" Margin="0 30 0 0" />
<Button Name="btnCancelTaskWithReturn" Content="取消“執行一個帶返回值的任務”" Click="btnCancelTaskWithReturn_Click_1" Margin="0 10 0 0" />
</StackPanel>
</Grid>
</Page>
Thread/Tasks/TaskDemo.xaml.cs
/*
* Task - 基於線程池的任務(在 System.Threading.Tasks 命名空間下)
*/
using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using System.Threading.Tasks;
using System.Threading;
using Windows.UI.Core;
namespace XamlDemo.Thread.Tasks
{
public sealed partial class TaskDemo : Page
{
/*
* CancellationTokenSource - 用於取消 CancellationToken
* Token - 一個 CancellationToken 類型的對象,用於關聯 Task
* IsCancellationRequested - 是否收到了取消操作的請求
* Cancel() - 發出取消操作的請求
*
* CancellationToken - 用於關聯 Task,以便取消 Task
* IsCancellationRequested - 是否收到了取消操作的請求
* WaitHandle - 信號,可以通過 WaitHandle.WaitOne() 在當前線程等待
* ThrowIfCancellationRequested() - 如果收到了取消操作的請求,則拋出一個OperationCanceledException 異常
*/
private CancellationTokenSource _cts;
public TaskDemo()
{
this.InitializeComponent();
}
private void btnCreateTask_Click_1(object sender, RoutedEventArgs e)
{
_cts = new CancellationTokenSource();
// 實例化一個 Task,可隨時通過 task.Status 獲取任務狀態
Task task = new Task(
(ctx) => // 任務所調用的方法,沒有返回值
{
// 在當前線程上阻塞 3000 毫秒(當收到取消請求時會發出信號,停止阻塞)
_cts.Token.WaitHandle.WaitOne(3000);
// 收到取消操作的請求後拋出一個 OperationCanceledException 異常,其會導致task.IsCanceled 的值變為 true
// 此處的代碼等同於 _cts.Token.ThrowIfCancellationRequested();
if (_cts.IsCancellationRequested)
throw new OperationCanceledException(_cts.Token);
},
null, // 上下文對象,task.AsyncState 可獲取到此對象,上面的 ctx 也可獲取到此對象
_cts.Token // 關聯的 CancellationToken 對象,用於取消操作
);
// 開始執行任務
task.Start();
// task.Wait(); 在當前線程上等待任務執行完
lblMsg.Text = "執行了一個沒有返回值的任務,3 秒後執行完畢";
// 任務執行完畢後的處理(注:ContinueWith 方法支持任意次回調,即可以寫多個task.ContinueWith() 都會被回調)
task.ContinueWith(
(ctx) => // 任務執行完畢後所調用的方法
{
if (ctx.IsCanceled) // 任務被取消
{
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "取消了“執行一個沒有返回值的任務”";
});
}
if (ctx.IsFaulted) // 任務發生了一個未處理異常
{
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "“執行一個沒有返回值的任務”發生了一個未處理異常
";
});
}
if (ctx.IsCompleted) // 任務已完成(任務成功地執行完畢或被取消或發生了未處理異常都會 ctx.IsCompleted == true)
{
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "“執行一個沒有返回值的任務”執行完成,taskId: " + ctx.Id.ToString();
});
}
});
}
private void btnCancelTask_Click_1(object sender, RoutedEventArgs e)
{
// 發出取消操作的請求
_cts.Cancel();
// _cts.CancelAfter(1000); // 1000 毫秒後發出取消操作的請求
}
private void btnCreateTaskWithReturn_Click_1(object sender, RoutedEventArgs e)
{
_cts = new CancellationTokenSource();
Func<object, string> handler = delegate(object state) // state 是傳遞過來的上下文對象
{
// 在當前線程上阻塞 3000 毫秒(當收到取消請求時會發出信號,停止阻塞)
_cts.Token.WaitHandle.WaitOne(3000);
// 收到取消操作的請求後拋出一個 OperationCanceledException 異常,其會導致task.IsCanceled 的值變為 true
// 此處的代碼等同於 _cts.Token.ThrowIfCancellationRequested();
if (_cts.IsCancellationRequested)
throw new OperationCanceledException(_cts.Token);
return "我是“執行一個帶返回值的任務”的返回值";
};
// Task.Factory.StartNew() - 創建任務並馬上執行,可隨時通過 task.Status 獲取任務狀態
// Task.Run() 同樣是創建任務並馬上執行
Task<string> task = Task.Factory.StartNew<string>(
handler, // 任務所調用的方法,帶返回值
null, // 上下文對象,task.AsyncState 可獲取到此對象
_cts.Token // 關聯的 CancellationToken 對象,用於取消操作
);
lblMsg.Text = "執行了一個帶返回值的任務,3 秒後執行完畢";
// 任務執行完畢後的處理(注:ContinueWith 方法支持任意次回調,即可以寫多個task.ContinueWith() 都會被回調)
task.ContinueWith(
(ctx) =>
{
if (ctx.IsCanceled) // 任務被取消
{
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "取消了“執行一個帶返回值的任務”";
});
}
if (ctx.IsFaulted) // 任務發生了一個未處理異常
{
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "“執行一個帶返回值的任務”發生了一個未處理異常";
});
}
if (ctx.IsCompleted) // 任務已完成(任務成功地執行完畢或被取消或發生了未處理異常都會 ctx.IsCompleted == true)
{
var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
lblMsg.Text += Environment.NewLine;
lblMsg.Text += "“執行一個帶返回值的任務”執行完成,taskId: " + ctx.Id.ToString();
});
// 當任務成功地執行完畢時,輸出任務的返回值
if (!ctx.IsCanceled && !ctx.IsFaulted)
{
ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High,
() =>
{
lblMsg.Text += Environment.NewLine;
// 任務的返回值
lblMsg.Text += ctx.Result;
});
}
}
});
}
private void btnCancelTaskWithReturn_Click_1(object sender, RoutedEventArgs e)
{
// 發出取消操作的請求
_cts.Cancel();
// _cts.CancelAfter(1000); // 1000 毫秒後發出取消操作的請求
}
}
}
2、演示多 Task 的並行執行
Thread/Tasks/MultiTask.xaml
<Page
x:Class="XamlDemo.Thread.Tasks.MultiTask"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XamlDemo.Thread.Tasks"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="Transparent">
<StackPanel Margin="120 0 0 0">
<TextBlock Name="lblMsg" FontSize="14.667" />
<Button Name="btnCreateMultiTask" Content="任務並行執行" Click="btnCreateMultiTask_Click_1" Margin="0 10 0 0" />
</StackPanel>
</Grid>
</Page>
Thread/Tasks/MultiTask.xaml.cs
/*
* 演示多 Task 的並行執行
*
* 注:
* 本例中同時創建了三個任務 task1, task2, task3,但是由於 Task 是基於線程池的,所以三個任務的啟動時間是不一樣的,啟動順序是不一定的
* 啟動順序可能是 task1->task2->task3,也可能是 task3->task2->task1,也可能是 task2
->task3->task1,等等等等都有可能,是不一定的
*/
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace XamlDemo.Thread.Tasks
{
public sealed partial class MultiTask : Page
{
private static int _count = 0;
public MultiTask()
{
this.InitializeComponent();
}
private void btnCreateMultiTask_Click_1(object sender, RoutedEventArgs e)
{
// 創建並執行任務1
Task task1 = Task.Run(
() =>
{
new System.Threading.ManualResetEvent(false).WaitOne(3000);
System.Threading.Interlocked.Increment(ref _count);
});
// 創建並執行任務2
Task task2 = Task.Run(
() =>
{
new System.Threading.ManualResetEvent(false).WaitOne(3000);
System.Threading.Interlocked.Increment(ref _count);
});
// 創建並執行任務3
Task task3 = Task.Run(
() =>
{
new System.Threading.ManualResetEvent(false).WaitOne(3000);
System.Threading.Interlocked.Increment(ref _count);
});
// 將所有任務合成一個 Task 對象,不會阻塞 UI 線程,通過 task.ContinueWith() 獲取結果
Task task = Task.WhenAll(task1, task2, task3);
// Task.WaitAll(task1, task2, task3); 等待所有任務完成,會阻塞 UI 線程
DateTime dt = DateTime.Now;
// task 執行完畢後的處理,即所有任務執行完畢後的處理
task.ContinueWith(
(ctx) =>
{
var ignored = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High,
() =>
{
lblMsg.Text = "count: " + _count.ToString() + ", 執行