天天看點

基礎才是重中之重~ThreadStatic靜态字段在每個線程裡的唯一性

static修飾符我們不會陌生,它代表靜态,可以修飾你的類,方法,字段和屬性等等,今天主要說一個為靜态字段加ThreadStatic特性會給程式代來什麼樣的變化。靜态字段static field,我更習慣稱它為“類的字段”,即它與類的具體執行個體無關,對于所有線程裡,它的值都是一個,即它的唯一性。

如代碼:

class Instance
{
  static DataContext context=new LinqToSql(conn);
  protected  static DataContext {get{return context;}}

}      

上面的代碼中,context為靜态字段,它的值在所有線程中都是一樣的,換名話說,在多個使用者通路一個頁面裡,得到它的值都是一樣的。

上面代碼是一個linq to sql實作DAL層時的一個基類,它向子類公開一個資料上下文,在所有使用者通路裡(所有線程中)其值(記憶體位址)都是一樣的,而在LINQTOSQL架構中,這個靜态資料上下文在實作開發中會出現很多問題,是以,官方不推薦使用。

但是,如果我們引入ThreadStatic之後,結果就不同了,當字段被ThreadStatic特性修飾後,它的值在每個線程中都是不同的,即每個線程對static字段都會重新配置設定記憶體空間,就當然于一次new操作,這樣一來,由于static字段所産生的問題也就沒有了,這種static資料上下文是可以被接受的。

internal class DbFactory
    {

        #region Fields
        /// <summary>
        /// 每個線程,一個新的DataContext執行個體
        /// </summary>
        [ThreadStatic]
        static readonly DataContext current = new DataClasses1DataContext();
        #endregion

        #region Contructors
        #endregion

        #region Properties
        public static DataContext Current
        {
            get { return current; }
        }
        #endregion

        #region Methods

        #endregion

        #region Events

        #endregion

    }      

上面代碼是一個生成LINQ上下文的工廠,你可以為每個LINQTOSQL類建立個基類,用來得到這個上下文,而具體操作類再去繼承這個基類,這在微軟的架構中,

會很常見,如System.Web.MVC.ControllerBase,它是Controller的基類。

/// <summary>
    /// DataClasses1DataContext資料庫基類
    /// </summary>
    public abstract class DbBase
    {

        #region Fields

        #endregion

        #region Contructors

        #endregion

        #region Properties
        public DataClasses1DataContext Db = DbFactory.Current as DataClasses1DataContext;
        #endregion

        #region Methods

        #endregion

        #region Events

        #endregion

    }      

而具體表的CURD操作,你可以單獨建立類檔案,來做這件事

public class WebManageUsers_Ext : WebManageUsers { }
    public class UserDAL : DbBase
    {

        #region Fields

        #endregion

        #region Contructors

        #endregion

        #region Properties

        #endregion

        #region Methods
        public IQueryable<WebManageUsers> GetModel()
        {
            var linq = from data1 in base.Db.WebManageUsers
                       join data2 in new DeptDAL().GetModel() on data1.DepartmentID equals data2.DepartmentID
                       select new WebManageUsers_Ext
                       {
                           RealName = data1.RealName,
                           WebDepartments = data2,
                       };
            return linq;
        }
        #endregion

        #region Events

        #endregion

    }      

OK,如果将上面代碼輸出後,它的結果是正确的

基礎才是重中之重~ThreadStatic靜态字段在每個線程裡的唯一性

事實上,上面我使用的複雜查詢,對于這種查詢,如果你的資料上下文不是static類型,它會出現異常的,一般異常為”為不能處理不同資料上下文的引用”,當然,這個提示是正常的,因為如果你的上下文為執行個體對象,那麼,對于每個類來說,它都是不同的,都會被new一次!

當出現上面問題後,我們往往解決方法是使用static類型的上下文,而static類型本身在LINQTOSQL上就是有問題的,在進行資料更新操作時,所有線程,所有操作的LINQ緩存都是一個,在submitChange時,會出現提求混亂的情況,錯誤是不可預知的。是以,你不得不把所有的表關系查詢都寫在DAL層,将每個業務的複雜查詢方法都重寫一次,這樣才能解決“不能資料上下文”的問題,太可怕了,呵呵。

當然,你如果提前做了表關聯,使用LINQ的“立即加載”也是一個不錯的方式,它可以為你節省不少代碼,但它産生的SQL語句,其内查詢則是select *  from table的格式,即傳回了很多無用的列。