多線程之線程同步: 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