多線程之線程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock
介紹
重新想象 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
[源碼下載]:http://files.cnblogs.com/webabcd/Windows8.rar