天天看點

不允許啟動新事務,因為有其他線程正在該會話中運作(ef并發錯誤處理之一)

最近項目上線試運作過程中,總是遇到各種莫名其妙的問題,糾結了很久,今天就ef出現的“不允許啟動新事務,因為有其他線程正在該會話中運作”這個錯誤,記錄自己的解決方法。

當遇到這個問題的時候,在網上搜了很久,在這裡還是要鄙視一下那些轉載又不寫出處的站長。

問題發現:

開始一直以為是資料庫或者伺服器的問題,因為用的是虛拟空間,很多設定沒有辦法改變。後來加入站長統計之後,發現pv量居然在200左右,突然意識到有可能是并發造成的。于是自己寫了一段測試代碼,該段代碼的作用是模拟使用者閱讀新聞的時候,使新聞的閱讀次數加1(隻是改變字段的值,而不是新增一條浏覽記錄再去統計閱讀次數)。代碼如下:

public partial class WebForm1 : System.Web.UI.Page
    {
        delegate bool MyDelegate(Read info);

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                ReadInfo ri = new ReadInfo ();

                Read rad1 = ri.Get(1);
                rad1.ReadTime += 1;

                Read rad2 = ri.Get(1);
                rad2.ReadTime += 1;

                MyDelegate myDelegate = new MyDelegate(ri.Update);

                myDelegate.BeginInvoke(rad1, null, null);
                myDelegate.BeginInvoke(rad2, null, null);

                Thread.Sleep(300);
            }
        }

    }

    public class ReadInfo
    {
        testEntities mytest = new testEntities();

        public ReadInfo()
        {
            mytest.Read.MergeOption = System.Data.Objects.MergeOption.OverwriteChanges;
            mytest.Read.EnablePlanCaching = false;
        }

        public Read Get(int Id)
        {
            return mytest.Read.SingleOrDefault(c => c.myId == Id);
        }

        public bool Update(Read Info)
        {
            int Result = 0;

            if (mytest.Read.Count(c => c.myId == Info.myId) == 1)
            {
                Thread.Sleep(100);
                Result = mytest.SaveChanges();
            }

            return Result > 0;
        }
    }
           

F10跟蹤代碼運作就報以下錯誤:

不允許啟動新事務,因為有其他線程正在該會話中運作(ef并發錯誤處理之一)

于是監控以下代碼發現并發産生的時候,第一次是Modified,第二次卻是Unchanged,是以當運作第二次更新的時候,則報錯“不允許啟動新事務,因為有其他線程正在該會話中運作”

不允許啟動新事務,因為有其他線程正在該會話中運作(ef并發錯誤處理之一)

是以最後解決此問題的方法就是将Update的代碼改成:

public bool Update(Read Info)
        {
            int Result = 0;

            if (mytest.Read.Count(c => c.myId == Info.myId) == 1)
            {
                if (Info.EntityState == System.Data.EntityState.Modified)
                {
                    mytest.Refresh(RefreshMode.ClientWins, Info);
                    Thread.Sleep(100);
                    Result = mytest.SaveChanges();
                }
            }

            return Result > 0;
        }
           

雖然這樣解決可以保證程式的穩定性,但是當并發發生的時候,閱讀次數就不能+1了。不過閱讀次數這個屬性對于網站的資料而言并不是很重要,是以先這麼解決着吧。如果是比較重要的屬性,建議還是通過add方法去增加記錄,而不是修改記錄。或者通過用存儲過程,将資料重新刷進表中,但是這樣就缺乏實時性。

真是蛋痛的并發!以後再想想,看看有沒有其他的解決辦法吧。

繼續閱讀