重新想象 Windows 8 Store Apps (47) - 多线程之线程同步: Semaphore,
CountdownEvent, Barrier, ManualResetEvent, AutoResetEvent
作者:webabcd
介绍
重新想象 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
[源码下载]