多線程之線程同步: Semaphore, CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent
介紹
重新想象 Windows 8 Store Apps 之 線程同步
Semaphore - 信號量
CountdownEvent - 通過信號數量實現線程同步
Barrier - 屏障
ManualResetEvent - 手動紅綠燈
AutoResetEvent - 自動紅綠燈
示例
1、演示 Semaphore 的使用
Thread/Lock/SemaphoreDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.SemaphoreDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Thread.Lock" 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" /> </StackPanel> </Grid> </Page>
Thread/Lock/SemaphoreDemo.xaml.cs
/* * 演示 Semaphore 的使用 * * Semaphore - 信號量 * SemaphoreSlim - 輕量級的 Semaphore * * 注: * 直譯 Semaphore 的話不太好理解,可以將 Semaphore 理解為一個許可證中心,該許可證中心的許可證數量是有限的 * 線程想要執行就要先從許可證中心獲取一個許可證(如果許可證中心的許可證已經發完了,那就等著,等著其它線程歸還許可證),執行完了再還回去 */ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Thread.Lock { public sealed partial class SemaphoreDemo : Page { /* * Semaphore(int initialCount, int maximumCount, string name) * initialCount - 許可證中心初始擁有的許可證數量,即初始情況下已經發出的許可證數量為maximumCount - initialCount * maximumCount - 許可證中心總共擁有的許可證數量 * name - 許可證中心的名稱 * Semaphore OpenExisting(string name) - 打開指定名稱的許可證中心 */ // 實例化一個許可證中心,該中心擁有的許可證數量為 2 個 private Semaphore _semaphore = new Semaphore(2, 2); public SemaphoreDemo() { this.InitializeComponent(); } protected async override void OnNavigatedTo(NavigationEventArgs e) { List<Task> tasks = new List<Task>(); // 模擬 5 個線程並行執行,拿到許可證的線程才能運行,而許可證中心只有 2 個許可證 for (int i = 0; i < 5; i++) { CancellationToken token = new CancellationTokenSource().Token; Task task = Task.Run( () => { OutMsg(string.Format("task {0} 等待一個許可證", Task.CurrentId)); token.WaitHandle.WaitOne(5000); // WaitOne() - 申請許可證 _semaphore.WaitOne(); OutMsg(string.Format("task {0} 申請到一個許可證", Task.CurrentId)); token.WaitHandle.WaitOne(1000); OutMsg(string.Format("task {0} 歸還了一個許可證", Task.CurrentId)); // int Release() - 歸還許可證,返回值為:Release() 之前許可證中心可用的許可證數量 int ignored = _semaphore.Release(); // int Release(int releaseCount) - 指定釋放的信號量的次數(按本文的理解就是指定歸還的許可證數量) }, token); tasks.Add(task); } await Task.WhenAll(tasks); } private void OutMsg(string msg) { var ignored = Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.High, () => { lblMsg.Text += msg; lblMsg.Text += Environment.NewLine; }); } } }
2、演示 CountdownEvent 的使用
Thread/Lock/CountdownEventDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.CountdownEventDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Thread.Lock" 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" /> </StackPanel> </Grid> </Page>
Thread/Lock/CountdownEventDemo.xaml.cs
/* * 演示 CountdownEvent 的使用 * * CountdownEvent - 通過信號數量實現線程同步 */ using System.Threading; using System.Threading.Tasks; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Thread.Lock { public sealed partial class CountdownEventDemo : Page { private static int _count; public CountdownEventDemo() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { // 初始信號數量為 100 個 using (CountdownEvent countdown = new CountdownEvent(100)) { // AddCount(), AddCount(int signalCount) - 增加 1 個信號,或增加指定數量的信號 // Reset(), Reset(int count) - 重置為初始的信號數量,或重置為指定的信號數量 // Signal(), Signal(int signalCount) - 減少 1 個信號,或減少指定數量的信號 // CurrentCount - 獲取當前的信號數量 for (int i = 0; i < 100; i++) { Task task = Task.Run( () => { Interlocked.Increment(ref _count); // 減少 1 個信號 countdown.Signal(); }); } // 阻塞當前線程,直到 CountdownEvent 的信號數量變為 0 countdown.Wait(); lblMsg.Text = "count: " + _count.ToString(); } } } }
3、演示 Barrier 的使用
Thread/Lock/BarrierDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.BarrierDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Thread.Lock" 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" /> </StackPanel> </Grid> </Page>
Thread/Lock/BarrierDemo.xaml.cs
/* * 演示 Barrier 的使用 * * Barrier - 屏障 * * 按如下方式理解: * 1、Participant - 參與者 * 2、SignalAndWait() - 某一個參與者已經到達屏障了 * 3、所有參與者都到達屏障後,屏障解除 */ using System.Threading; using System.Threading.Tasks; using Windows.UI.Core; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Thread.Lock { public sealed partial class BarrierDemo : Page { private static int _count; public BarrierDemo() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { // AddParticipant(), AddParticipants(int participantCount) - 增加 1 個參與者,或增加指定數量的參與者 // RemoveParticipant(), RemoveParticipants(int participantCount) - 減少 1 個參與者,或減少指定數量的參與者 // ParticipantCount - 獲取參與者總數 // ParticipantsRemaining - 尚未到達屏障的參與者總數 Barrier barrier = new Barrier( 5, // 初始有 5 個參與者 (ctx) => // 屏障解除之後 { var ignored = Dispatcher.RunAsync(CoreDispatcherPriority.High, () => { lblMsg.Text = "count: " + _count.ToString(); }); }); for (int i = 0; i < 5; i++) { Task task = Task.Run( () => { Interlocked.Increment(ref _count); // 某一個參與者已經到達屏障了 barrier.SignalAndWait(); // SignalAndWait(int millisecondsTimeout) - 指定一個超時時間 // SignalAndWait(CancellationToken cancellationToken) - 指定一個 CancellationToken }); } } } }
4、演示 ManualResetEvent 的使用
Thread/Lock/ManualResetEventDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.ManualResetEventDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Thread.Lock" 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" /> </StackPanel> </Grid> </Page>
Thread/Lock/ManualResetEventDemo.xaml.cs
/* * 演示 ManualResetEvent 的使用 * * ManualResetEvent - 手動紅綠燈 * ManualResetEventSlim - 輕量級的 ManualResetEvent */ using System; using System.Threading; using System.Threading.Tasks; using Windows.Foundation; using Windows.System.Threading; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Thread.Lock { public sealed partial class ManualResetEventDemo : Page { // true - 指定初始狀態為綠燈 private ManualResetEvent _manualResetEvent = new ManualResetEvent(true); private static int _count = 0; public ManualResetEventDemo() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { ManualResetEvent sleep = new ManualResetEvent(false); Task tas = Task.Run( () => { while (true) { // WaitOne() - 判斷:綠燈則進入,紅燈則阻塞 _manualResetEvent.WaitOne(); /* * WaitOne(1000) * 1、如果當前是綠燈則進入 * 2、如果當前是紅燈則阻塞 * a) 1000 毫秒之內收到 Set() 信號則進入 * b) 1000 毫秒之後如果還沒收到 Set() 信號則強行進入 */ IAsyncAction ignored = Windows.System.Threading.ThreadPool.RunAsync( (threadPoolWorkItem) => { // 在當前線程 sleep 1000 毫秒(WinRT 中沒有 Thread.Sleep() 了) sleep.WaitOne(1000); OutMsg(string.Format("count:{0}, time:{1}", ++_count, DateTime.Now.ToString("mm:ss"))); // Set() - 發出綠燈信號,並設置為綠燈 _manualResetEvent.Set(); }, WorkItemPriority.High); // Reset() - 發出紅燈信號,並設置為紅燈 _manualResetEvent.Reset(); } }); } private void OutMsg(string msg) { var ignored = Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.High, () => { lblMsg.Text = msg; }); } } }
5、演示 AutoResetEvent 的使用
Thread/Lock/AutoResetEventDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.AutoResetEventDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:XamlDemo.Thread.Lock" 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" /> </StackPanel> </Grid> </Page>
Thread/Lock/AutoResetEventDemo.xaml.cs
/* * 演示 AutoResetEvent 的使用 * * AutoResetEvent - 自動紅綠燈 * * AutoResetEvent 和 ManualResetEvent 的區別在於:AutoResetEvent 在 WaitOne() 進入之後會自動 Reset() */ using System; using System.Threading; using System.Threading.Tasks; using Windows.Foundation; using Windows.System.Threading; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Thread.Lock { public sealed partial class AutoResetEventDemo : Page { // true - 指定初始狀態為綠燈 private AutoResetEvent _autoResetEvent = new AutoResetEvent(true); private static int _count = 0; public AutoResetEventDemo() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { ManualResetEvent sleep = new ManualResetEvent(false); Task tas = Task.Run( () => { while (true) { // WaitOne() - 判斷:綠燈則進入,紅燈則阻塞,進入之後自動 Reset() _autoResetEvent.WaitOne(); /* * WaitOne(1000) * 1、如果當前是綠燈則進入,進入之後自動 Reset() * 2、如果當前是紅燈則阻塞 * a) 1000 毫秒之內收到 Set() 信號則進入,進入之後自動 Reset() * b) 1000 毫秒之後如果還沒收到 Set() 信號則強行進入,進入之後自動 Reset() */ IAsyncAction ignored = Windows.System.Threading.ThreadPool.RunAsync( (threadPoolWorkItem) => { // 在當前線程 sleep 1000 毫秒(WinRT 中沒有 Thread.Sleep() 了) sleep.WaitOne(1000); OutMsg(string.Format("count:{0}, time:{1}", ++_count, DateTime.Now.ToString("mm:ss"))); // Set() - 發出綠燈信號,並設置為綠燈 _autoResetEvent.Set(); }, WorkItemPriority.High); } }); } private void OutMsg(string msg) { var ignored = Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.High, () => { lblMsg.Text = msg; }); } } }
OK
[源碼下載]:http://files.cnblogs.com/webabcd/Windows8.rar