重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock
作者:webabcd
介绍
重新想象 Windows 8 Store Apps 之 线程同步
- lock - 其实就是对 Monitor.Enter() 和 Monitor.Exit() 的一个封装
- Monitor - 锁
- Interlocked - 为多个线程共享的数字型变量提供原子操作
- Mutex - 互斥锁,主要用于同一系统内跨进程的互斥锁
- ReaderWriterLock - 读写锁
示例
1、演示 lock 的使用
Thread/Lock/LockDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.LockDemo" 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="lblMsgWithoutLock" FontSize="14.667" /> <TextBlock Name="lblMsgWithLock" FontSize="14.667" /> </StackPanel> </Grid> </Page>
Thread/Lock/LockDemo.xaml.cs
/* * 演示 lock 的使用 * * 注:lock 其实就是对 Monitor.Enter() 和 Monitor.Exit() 的一个封装 */ using System.Collections.Generic; using System.Threading.Tasks; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Thread.Lock { public sealed partial class LockDemo : Page { // 需要被 lock 的对象 private static readonly object _objLock = new object(); private static int _countWithoutLock; private static int _countWithLock; public LockDemo() { this.InitializeComponent(); } protected async override void OnNavigatedTo(NavigationEventArgs e) { List<Task> tasks = new List<Task>(); // 一共 100 个任务并行执行,每个任务均累加同一个静态变量 100000 次,以模拟并发访问静态变量的场景 for (int i = 0; i < 100; i++) { Task task = Task.Run( () => { /******************有锁的逻辑开始******************/ try { // 通过 lock 锁住指定的对象以取得排它锁,在 lock 区域内的代码执行完毕后释放排它锁,排它锁释放之前其它进入到此的线程会排队等候 lock (_objLock) { for (int j = 0; j < 100000; j++) { _countWithLock++; } } } finally { } /******************有锁的逻辑结束******************/ /******************没锁的逻辑开始******************/ for (int j = 0; j < 100000; j++) { _countWithoutLock++; } /******************没锁的逻辑结束******************/ }); tasks.Add(task); } // 等待所有任务执行完毕 await Task.WhenAll(tasks); lblMsgWithoutLock.Text = "计数器(不带锁)结果:" + _countWithoutLock.ToString(); lblMsgWithLock.Text = "计数器(带锁)结果:" + _countWithLock.ToString(); } } }
2、演示 Monitor 的使用
Thread/Lock/MonitorDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.MonitorDemo" 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/MonitorDemo.xaml.cs
/* * 演示 Monitor 的使用 * * 本例说明: * 由于 Task 基于线程池,所以 task1 和 task2 的启动顺序是不一定的,以下步骤假定 task1 先执行,task2 后执行 * 1、task1 取得排它锁 * 2、task1 Monitor.Wait() - 释放排它锁,然后 task1 进入等待队列,可以为其指定一个超时时间,超过则进入就绪队列 * 3、task2 取得排它锁 * 4、task2 Monitor.Pulse() - 让等待队列中的一个线程进入就绪队列(Monitor.PulseAll() 的作用是将等待队列中的全部线程全部放入就绪队列) * 5、task1 进入就绪队列 * 6、task2 Monitor.Wait() - 释放排它锁,然后 task2 进入等待队列 * 7、task1 取得排它锁 * 8、以上步骤不断往复 * * 注: * 1、Wait() 和 Pulse() 必须在 Enter() 和 Exit() 之间,或者在 lock(){ } 中 * 2、只有就绪队列中的线程才能取得排它锁,等待队列中的线程是无法取得排它锁的 */ 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 MonitorDemo : Page { // 需要被 lock 的对象 private static readonly object _objLock = new object(); public MonitorDemo() { this.InitializeComponent(); } protected async override void OnNavigatedTo(NavigationEventArgs e) { string result = ""; // 在 task1 中执行则为 true,在 task2 中执行则为 false bool flag = true; Task task1 = Task.Run( () => { try { // 在指定的对象上取得排它锁 Monitor.Enter(_objLock); for (int i = 0; i < 10; i++) { if (flag) Monitor.Wait(_objLock); flag = true; result += string.Format("task1 i:{0}, taskId:{1}", i, Task.CurrentId); result += Environment.NewLine; Monitor.Pulse(_objLock); } } finally { // 在指定的对象上释放排它锁 Monitor.Exit(_objLock); } }); Task task2 = Task.Run( () => { try { // 在指定的对象上取得排它锁 Monitor.Enter(_objLock); for (int i = 0; i < 10; i++) { if (!flag) Monitor.Wait(_objLock); flag = false; result += string.Format("task2 i:{0}, taskId:{1}", i, Task.CurrentId); result += Environment.NewLine; Monitor.Pulse(_objLock); } } finally { // 在指定的对象上释放排它锁 Monitor.Exit(_objLock); } }); await Task.WhenAll(task1, task2); lblMsg.Text = result; } } }
3、演示 Interlocked 的使用
Thread/Lock/InterlockedDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.InterlockedDemo" 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="lblMsgWithoutLock" FontSize="14.667" /> <TextBlock Name="lblMsgWithLock" FontSize="14.667" /> </StackPanel> </Grid> </Page>
Thread/Lock/InterlockedDemo.xaml.cs
/* * 演示 Interlocked 的使用 * * Interlocked - 为多个线程共享的数字型变量提供原子操作,其提供了各种原子级的操作方法,如:增减变量、比较变量、指定变量的值 * * 注: * long Read(ref long location) - 用于在 32 位系统上以原子方式读取 64 位值(32 位系统访问 32 位值本身就是原子的,64 位系统访问 64 位值本身就是原子的) */ 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 InterlockedDemo : Page { private static int _countWithoutLock; private static int _countWithLock; public InterlockedDemo() { this.InitializeComponent(); } protected async override void OnNavigatedTo(NavigationEventArgs e) { List<Task> tasks = new List<Task>(); // 一共 100 个任务并行执行,每个任务均累加同一个静态变量 100000 次,以模拟并发访问静态变量的场景 for (int i = 0; i < 100; i++) { Task task = Task.Run( () => { /******************有锁的逻辑开始******************/ for (int j = 0; j < 100000; j++) { // 原子方式让 _countWithLock 加 1 Interlocked.Increment(ref _countWithLock); } /******************有锁的逻辑结束******************/ /******************没锁的逻辑开始******************/ for (int j = 0; j < 100000; j++) { _countWithoutLock++; } /******************没锁的逻辑结束******************/ }); tasks.Add(task); } await Task.WhenAll(tasks); lblMsgWithoutLock.Text = "计数器(不带锁)结果:" + _countWithoutLock.ToString(); lblMsgWithLock.Text = "计数器(带锁)结果:" + _countWithLock.ToString(); } } }
4、演示 Mutex 的使用
Thread/Lock/MutexDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.MutexDemo" 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="lblMsgWithoutLock" FontSize="14.667" /> <TextBlock Name="lblMsgWithLock" FontSize="14.667" /> </StackPanel> </Grid> </Page>
Thread/Lock/MutexDemo.xaml.cs
/* * 演示 Mutex 的使用 * * Mutex - 互斥锁,主要用于同一系统内跨进程的互斥锁 */ 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 MutexDemo : Page { private Mutex _mutex = new Mutex(); private static int _countWithoutLock; private static int _countWithLock; public MutexDemo() { this.InitializeComponent(); } protected async override void OnNavigatedTo(NavigationEventArgs e) { List<Task> tasks = new List<Task>(); // 一共 100 个任务并行执行,每个任务均累加同一个静态变量 100000 次,以模拟并发访问静态变量的场景 for (int i = 0; i < 100; i++) { Task task = Task.Run( () => { /******************有锁的逻辑开始******************/ // 当前线程拿到 Mutex,阻塞当前线程,可以指定阻塞的超时时间 _mutex.WaitOne(); for (int j = 0; j < 100000; j++) { _countWithLock++; } // 释放 Mutex _mutex.ReleaseMutex(); /******************有锁的逻辑结束******************/ /******************没锁的逻辑开始******************/ for (int j = 0; j < 100000; j++) { _countWithoutLock++; } /******************没锁的逻辑结束******************/ }); tasks.Add(task); } await Task.WhenAll(tasks); lblMsgWithoutLock.Text = "计数器(不带锁)结果:" + _countWithoutLock.ToString(); lblMsgWithLock.Text = "计数器(带锁)结果:" + _countWithLock.ToString(); } } }
5、演示 ReaderWriterLockSlim 的使用
Thread/Lock/ReaderWriterLockDemo.xaml
<Page x:Class="XamlDemo.Thread.Lock.ReaderWriterLockDemo" 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="lblMsgForRead" FontSize="14.667" /> <TextBlock Name="lblMsgForWrite" FontSize="14.667" /> </StackPanel> </Grid> </Page>
Thread/Lock/ReaderWriterLockDemo.xaml.cs
/* * 演示 ReaderWriterLockSlim 的使用 * * ReaderWriterLock - 读写锁(WinRT 中不提供) * ReaderWriterLockSlim - 轻量级的 ReaderWriterLock * 支持进入/离开读锁,进入/离开写锁,读锁升级为写锁 * 支持相关状态的获取,如:当前线程是否进入了读锁以及进入读锁的次数,是否进入了写锁以及进入写锁的次数,是否由读锁升级为了写锁以及由读锁升级为写锁的次数 * * 注: * 1、每次可以有多个线程进入读锁 * 2、每次只能有一个线程进入写锁 * 3、进入写锁后,无法进入读锁 * * * 本例模拟了一个“高频率读,低频率写”的场景 */ using System; using System.Threading; using Windows.System.Threading; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; namespace XamlDemo.Thread.Lock { public sealed partial class ReaderWriterLockDemo : Page { ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); public ReaderWriterLockDemo() { this.InitializeComponent(); } protected override void OnNavigatedTo(NavigationEventArgs e) { ThreadPoolTimer.CreatePeriodicTimer( (timer) => { // 进入读锁 _rwLock.EnterReadLock(); OutMsgForRead("读:" + DateTime.Now.ToString("mm:ss.fff")); // 离开读锁 _rwLock.ExitReadLock(); }, TimeSpan.FromMilliseconds(100)); ThreadPoolTimer.CreatePeriodicTimer( (timer) => { // 进入写锁 _rwLock.EnterWriteLock(); new ManualResetEvent(false).WaitOne(3000); // 本线程停 3000 毫秒 OutMsgForWrite("写:" + DateTime.Now.ToString("mm:ss.fff")); // 离开写锁 _rwLock.ExitWriteLock(); }, TimeSpan.FromMilliseconds(5000)); } private async void OutMsgForRead(string msg) { await Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.High, () => { lblMsgForRead.Text = msg; }); } private async void OutMsgForWrite(string msg) { await Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.High, () => { lblMsgForWrite.Text = msg; }); } } }
OK
[源码下载]