並發控制(鎖)(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