C# 線程手冊 第三章 使用線程 創建線程安全的包裝器(實戰篇)


在這部分我們將看兩個大的例子。首先,我們將看一下創建線程安全的包裝器的例子,然后看一下數據庫連接池例子。

實現自己的線程安全包裝器

實現自己的線程安全包裝器主要基於你可能不想讓類庫中的每個類都是線程安全的,而使用同步也會帶來性能問題的事實。你可能想要為開發人員提供一個是否使用一個同步類的選擇。由於開發人員既不想發生死鎖也不想發生由於在一個單線程環境中使用線程安全類而導致的性能問題,他們可能更傾向於為類庫中相同的類使用內建同步包裝器而不是為每個類分別實現一個。System.Collections命名空間中的ArrayList和Hashtable集合類早就有了這個特性。你可以決定在初始化一個Hashtable的時候決定是使用一個線程安全的還是非線程安全的。你可以通過像下面代碼那樣調用Hashtable類的共享Synchronized()方法來實現一個線程安全Hashtable.

Hashtable h = Hashtable.Synchronized(new Hashtable());

為開發人員提供這樣的一個選項是非常好的。在這個例子中,我們嘗試開發一個類以及這個類的同步包裝器。我們實現一個圖書集合類庫並使用圖4的UML形式來表示。

UML

圖 4

 

程序很簡單,但是使用固有同步支持的概念非常重要。通過為我們的類庫添加固有同步支持,我們將允許開發人員為同一個類選擇同步實現和非同步實現。例如,不需要使用同步方法的開發人員可以像下面這樣實例化一個對象:

BookLib acc = new BookLib();

而需要在多線程環境中使用線程安全包裝的開發人員可以如下實現:

BookLib acc = new BookLib();
acc = acc.Synchronized();

下面是帶同步包裝器的IBookCollection 源碼:

/*************************************
/* copyright (c) 2012 daniel dong
 * 
 * author:daniel dong
 * blog:  www.cnblogs.com/danielwise
 * email: guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BookLibrary
{
    interface IBookCollection
    {
        void Clear();
        void Add(Book n);
        Book GetBook(string ISBN);
        bool IsSynchronized { get; }
        object SyncRoot { get; }
    }
}

下面是BookLib源碼:

/*************************************
/* copyright (c) 2012 daniel dong
 * 
 * author:daniel dong
 * blog:  www.cnblogs.com/danielwise
 * email: guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;

namespace BookLibrary
{
    class BookLib : IBookCollection
    {
        internal Hashtable bk = new Hashtable(10);

        public virtual void Clear()
        {
            this.bk.Clear();
        }

        public virtual void Add(Book b)
        {
            Console.WriteLine("Adding Book for ThreadID: "
                + Thread.CurrentThread.GetHashCode());
            Thread.Sleep(2000);
            bk.Add(b.ISBN, b);
        }

        public virtual Book GetBook(string ISBN)
        {
            Console.WriteLine("Getting Book for ThreadID: "
                + Thread.CurrentThread.GetHashCode());
            return (Book)bk[ISBN];
        }

        public virtual bool IsSynchronized
        {
            get { return false; }
        }

        public virtual object SyncRoot
        {
            get { return this; }
        }

        public BookLib Synchronized()
        {
            return Synchronized(this);
        }

        public static BookLib Synchronized(BookLib bc)
        {
            if (bc == null)
            {
                throw new ArgumentException("bc");
            }

            if (bc.GetType() == typeof(SyncBookLib))
            {
                throw new InvalidOperationException(
                    "BookLib reference is already synchronized.");
            }

            return new SyncBookLib(bc);
        }

        public static IBookCollection Synchronized(IBookCollection acc)
        {
            if (acc == null)
            {
                throw new ArgumentException("acc");
            }

            if (acc.GetType() == typeof(SyncBookLib))
            {
                throw new InvalidOperationException(
                    "BookLib reference is already synchronized.");
            }

            return new SyncBookLib(acc);
        }
    }
}

下面是SyncBookLib源碼:

/*************************************
/* copyright (c) 2012 daniel dong
 * 
 * author:daniel dong
 * blog:  www.cnblogs.com/danielwise
 * email: guofoo@163.com
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BookLibrary
{
    sealed class SyncBookLib : BookLib
    {
        private object syncRoot;
        private object booklib;

        internal SyncBookLib(IBookCollection acc)
        {
            booklib = acc;
            syncRoot = acc.SyncRoot;
        }

        public override void Clear()
        {
            lock (syncRoot)
            {
                base.Clear();
            }
        }

        public override void Add(Book b)
        {
            lock (syncRoot)
            {
                base.Add(b);
            }
        }

        public override Book GetBook(string ISBN)
        {
            lock (syncRoot)
            {
                return base.GetBook(ISBN);
            }
        }

        public override object SyncRoot
        {
            get
            {
                return syncRoot;
            }
        }
    }
}

在上面的代碼中,我們首先聲明了一個接口IBookCollection, 它有下面的一些處理書籍集合的方法和屬性:

  Clear() - 清除書籍集合的方法

  Add() - 向集合中添加一本書的方法

  GetBook() - 從集合中獲取一本書的方法

  IsSynchronized() - 用來檢查集合是否為同步的只讀屬性

  SyncRoot() - 獲取集合同步根的只讀屬性

下面我們定義一個用來表示集合中的一本書的Book類。例如,集合可能是一個圖書館或者一家書店,但是Book類所表達的意義都是一樣的。

BookLib類實現了IBookCollection接口。所以BookLib必須實現IBookCollection接口的所有方法和屬性。我們定義了一個名為bk的Hashtable來作為書籍集合。Book對象的鍵值是ISBN碼。在Add()方法中,我們向哈希表中添加了一本書。在GetBook()方法中,如果提供了書籍的ISBN碼,我們將會得到書籍對象。

現在我們必須處理同步問題。在Synchronized()方法中,我們創建了一個SyncBookLib類型的對象並返回一個它的引用。SyncBookLib是BookLib類的同步版本。SyncBookLib繼承自BookLib類,也就繼承了BookLib類已經實現的所有屬性和方法。SyncBookLib和BookLib類的不同之處在於SyncBookLib類中我們使用lock鎖定了代碼的關鍵部分。例如,Clear(), GetBook() 和 Add() 方法的實現中都用到了鎖,所以它們都是線程安全的。而BookLib類的所有方法中都沒有鎖。

在測試類中,如果我們傳遞任何命令行參數那么就可以創建一個同步的BookLib實例。如果不傳遞任何參數則傳遞一個非線程安全的BookLib實例。當運行程序時。線程安全的BookLib實現和非線程安全的實現區別是非常明顯的。在線程安全版本中,在任何時間只能有一個線程訪問。所以其他兩個線程不得不等第一個線程處理完。而非線程安全的版本中所有線程都允許同時訪問BookLib對象實例。

帶命令行參數的BookLib(線程安全)的輸出結果如下:

threadsafe

不帶命令行參數的BookLib(非線程安全)輸出結果如下:

non-threadsafe

 

下一篇介紹一個數據庫連接池…


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM