並發控制(鎖)(Mutex, Semaphore, Monitor, Lock, ThreadPool, Interlocked, ReaderWriterLock)
介紹
WCF(Windows Communication Foundation) - 並發控制:以ConcurrencyMode.Multiple並發模式及InstanceContextMode.Single實例模型為例(此時有並發問題),介紹如何做並發控制,即各種鎖的使用(Mutex, Semaphore, Monitor, Lock, ThreadPool, Interlocked, ReaderWriterLock)
示例
1、服務
Enum.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;
namespace WCF.ServiceLib.ConcurrencyLock
{
/**//// <summary>
/// 鎖 類型的枚舉
/// </summary>
[DataContract]
public enum LockType
{
/**//// <summary>
/// 不使用任何並發控制
/// </summary>
[EnumMember]
None,
/**//// <summary>
/// Mutex
/// </summary>
[EnumMember]
Mutex,
/**//// <summary>
/// Semaphore
/// </summary>
[EnumMember]
Semaphore,
/**//// <summary>
/// Monitor
/// </summary>
[EnumMember]
Monitor,
/**//// <summary>
/// Lock
/// </summary>
[EnumMember]
Lock
}
}
IHello.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WCF.ServiceLib.ConcurrencyLock
{
/**//// <summary>
/// 演示並發控制(鎖)的接口
/// </summary>
[ServiceContract]
public interface IHello
{
/**//// <summary>
/// 計數器
/// </summary>
/// <param name="lockType">鎖的類型</param>
[OperationContract]
void Counter(LockType lockType);
/**//// <summary>
/// 獲取計數器被調用的結果
/// </summary>
/// <returns></returns>
[OperationContract]
string GetResult();
/**//// <summary>
/// 清空計數器和結果
/// </summary>
[OperationContract]
void CleanResult();
}
}
Hello.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WCF.ServiceLib.ConcurrencyLock
{
/**//// <summary>
/// 演示並發控制(鎖)的接口
/// </summary>
/// <remarks>
/// ServiceBehavior - 指定服務協定實現的內部執行行為
/// 實例模型:單例;並發模式:多線程
/// 會有並發問題,通過 鎖 來解決
/// </remarks>
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Hello : IHello
{
private int _counter;
private string _result;
private System.Threading.Mutex _mutex = new System.Threading.Mutex();
// 此構造函數初始化未命名的信號量。所有使用這類信號量的實例的線程都必須具有對該實例的引用。
// 如果 initialCount 小於 maximumCount,則效果與當前線程調用了 WaitOne(maximumCount 減去 initialCount)次相同。如果不想為創建信號量的線程保留任何入口,請對 maximumCount 和 initialCount 使用相同的數值。
private System.Threading.Semaphore _semaphore = new System.Threading.Semaphore(1, 1);
private static readonly object objLock = new object();
/**//// <summary>
/// 計數器
/// </summary>
/// <returns></returns>
public void Counter(LockType lockType)
{
switch (lockType)
{
case LockType.None:
ExecuteNone();
break;
case LockType.Mutex:
ExecuteMutex();
break;
case LockType.Semaphore:
ExecuteSemaphore();
break;
case LockType.Monitor:
ExecuteMonitor();
break;
case LockType.Lock:
ExecuteLock();
break;
}
}
/**//// <summary>
/// 獲取計數器被調用的結果
/// </summary>
/// <returns></returns>
public string GetResult()
{
return _result;
}
/**//// <summary>
/// 清空計數器和結果
/// </summary>
public void CleanResult()
{
_result = "";
_counter = 0;
}
/**//// <summary>
/// 循環調用技術器,以模擬並發
/// 結果中,出現重復計數,則有並發問題,反之,則無並發問題
/// </summary>
private void CircleCounter()
{
for (int i = 0; i < 10; i++)
{
var counter = _counter;
// 停20毫秒,以模擬並發
System.Threading.Thread.Sleep(20);
_counter = ++counter;
// 保存計數結果
_result += _counter + "|";
}
}
/**//// <summary>
/// 不使用任何並發控制
/// </summary>
private void ExecuteNone()
{
CircleCounter();
}
/**//// <summary>
/// Mutex的實現
/// </summary>
private void ExecuteMutex()
{
try
{
_mutex.WaitOne();
CircleCounter();
}
finally
{
_mutex.ReleaseMutex();
}
}
/**//// <summary>
/// Semaphore的實現
/// </summary>
private void ExecuteSemaphore()
{
try
{
_semaphore.WaitOne();
CircleCounter();
}
finally
{
_semaphore.Release();
}
}
/**//// <summary>
/// Monitor的實現
/// </summary>
private void ExecuteMonitor()
{
try
{
System.Threading.Monitor.Enter(this);
CircleCounter();
}
finally
{
System.Threading.Monitor.Exit(this);
}
}
/**//// <summary>
/// Lock的實現
/// </summary>
private void ExecuteLock()
{
try
{
lock (objLock)
{
CircleCounter();
}
}
finally
{
}
}
}
}
2、宿主
Hello.svc
<%@ ServiceHost Language="C#" Debug="true" Service="WCF.ServiceLib.ConcurrencyLock.Hello" %>
Web.config
<?xml version="1.0"?> <configuration> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="ConcurrencyLockBehavior"> <!--httpGetEnabled - 指示是否發布服務元數據以便使用 HTTP/GET 請求進行檢索,如果發布 WSDL,則為 true,否則為 false,默認值為 false--> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> <services> <!--name - 提供服務的類名--> <!--behaviorConfiguration - 指定相關的行為配置--> <service name="WCF.ServiceLib.ConcurrencyLock.Hello" behaviorConfiguration="ConcurrencyLockBehavior"> <!--address - 服務地址--> <!--binding - 通信方式--> <!--contract - 服務契約--> <endpoint address="" binding="basicHttpBinding" contract="WCF.ServiceLib.ConcurrencyLock.IHello" /> </service> </services> </system.serviceModel> </configuration>
3、客戶端
Hello.aspx
<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="Hello.aspx.cs" Inherits="ConcurrencyLock_Hello" Title="並發控制(鎖)(Mutex, Semaphore, Monitor, Lock, ThreadPool, Interlocked, ReaderWriterLock)" %> <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server"> <p> <asp:Button ID="btnCleanResult" runat="server" Text="清空結果" OnClick="btnCleanResult_Click" /> <asp:Button ID="btnHelloNone" runat="server" Text="HelloNone" OnCommand="btn_Command" CommandName="None" /> <asp:Button ID="btnHelloMutex" runat="server" Text="HelloMutex" OnCommand="btn_Command" CommandName="Mutex" /> <asp:Button ID="btnHelloSemaphore" runat="server" Text="HelloSemaphore" OnCommand="btn_Command" CommandName="Semaphore" /> <asp:Button ID="btnHelloMonitor" runat="server" Text="HelloMonitor" OnCommand="btn_Command" CommandName="Monitor" /> <asp:Button ID="btnHelloLock" runat="server" Text="HelloLock" OnCommand="btn_Command" CommandName="Lock" /> <br /> <ul> <li>None:不使用並發控制(有並發問題,會出現重復的計數)</li> <li>其他:使用相關的並發控制(無並發問題,不會出現重復的計數)</li> </ul> </p> <div> <asp:TextBox ID="txtResult" runat="server" TextMode="MultiLine" Style="width: 98%; height: 200px" /> </div> <div> <ul> <li>Mutex - 提供對資源的獨占訪問</li> <li>Semaphore - 限制可同時訪問某一資源或資源池的線程數</li> <li>Monitor - 提供同步訪問對象的機制</li> <li>Lock - 關鍵字將語句塊標記為臨界區,方法是獲取給定對象的互斥鎖,執行語句,然後釋放該鎖</li> <li>ThreadPool - 提供一個線程池,該線程池可用於發送工作項、處理異步 I/O、代表其他線程等待以及處理計時器</li> <li>Interlocked - 為多個線程共享的變量提供原子操作</li> <li>ReaderWriterLock - 定義支持單個寫線程和多個讀線程的鎖</li> </ul> </div> </asp:Content>
Hello.aspx.cs
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Threading;
public partial class ConcurrencyLock_Hello : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btn_Command(object sender, CommandEventArgs e)
{
// 線程1
var thread1 = new Thread(new ParameterizedThreadStart(Do));
thread1.Start(e.CommandName);
// 線程2
var thread2 = new Thread(new ParameterizedThreadStart(Do));
thread2.Start(e.CommandName);
for (int i = 0; i < 100; i++)
{
Thread.Sleep(100);
if (thread1.ThreadState == ThreadState.Stopped && thread2.ThreadState == ThreadState.Stopped)
{
// 返回服務端的技術器的調用結果
var proxy = new ConcurrencyLockSvc.HelloClient();
txtResult.Text = proxy.GetResult();
proxy.Close();
break;
}
}
}
private void Do(object commandName)
{
ConcurrencyLockSvc.LockType lockType = (ConcurrencyLockSvc.LockType)Enum.Parse(typeof(ConcurrencyLockSvc.LockType), (string)commandName);
// 調用服務端技術器
using (var proxy = new ConcurrencyLockSvc.HelloClient())
{
proxy.Counter(lockType);
}
}
protected void btnCleanResult_Click(object sender, EventArgs e)
{
// 清空計數器和結果
using (var proxy = new ConcurrencyLockSvc.HelloClient())
{
proxy.CleanResult();
}
txtResult.Text = "";
}
}
Web.config
<?xml version="1.0"?> <configuration> <system.serviceModel> <client> <!--address - 服務地址--> <!--binding - 通信方式--> <!--contract - 服務契約--> <endpoint address="http://localhost:3502/ServiceHost/ConcurrencyLock/Hello.svc" binding="basicHttpBinding" contract="ConcurrencyLockSvc.IHello" /> </client> </system.serviceModel> </configuration>
運行結果:
單擊"HelloNone"按鈕:不使用並發控制(有並發問題,會出現重復的計數)
單擊"HelloMutex", "HelloSemaphore", "HelloMonitor", "HelloLock"按鈕:使用相應的並發控制(無並發問題,不會出現重復的計數)
OK