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