最近項目上線試運行過程中,總是遇到各種莫名其妙的問題,糾結了很久,今天就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跟蹤代碼運行就報以下錯誤:
於是監控以下代碼發現併發產生的時候,第一次是Modified,第二次卻是Unchanged,所以當運行第二次更新的時候,則報錯“不允許啓動新事務,因爲有其他線程正在該會話中運行”
所以最後解決此問題的方法就是將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方法去增加記錄,而不是修改記錄。或者通過用存儲過程,將數據重新刷進表中,但是這樣就缺乏實時性。
真是蛋痛的併發!以後再想想,看看有沒有其他的解決辦法吧。