[英]Firebird in multi-thread environment
我已經創建了一個用於容納Firebird數據庫連接的單例類,並且正在執行查詢(請參見下文),該查詢的工作原理非常吸引人,直到我不得不向其中一個應用程序添加一些計時器來定期設置和檢查某些數據。
現在,我經常出錯,說“不支持並行事務”。 我不明白,因為我在finally塊中關閉了連接。
我已經花了很長時間來搜索網絡並試圖找出解決方法,但似乎我需要支持。
這是我的fBird
類的主要部分:
public sealed class fBird {
private FbConnection FbConn = new FbConnection(connectionString);
static fBird m_oInstance = null;
static readonly object m_oPadLock = new object();
public static fBird Instance {
get {
lock (m_oPadLock) {
if (m_oInstance == null) {
m_oInstance = new fBird();
}
return m_oInstance;
}
}
}
//Data query:
public DataTable qSelect(string query){
if(!getState().Equals("Open")) FbConn.Open();
using (FbTransaction transaction = FbConn.BeginTransaction()) {
try{
FbCommand cmd = new FbCommand(query, FbConn, transaction);
FbDataAdapter adpt = new FbDataAdapter(cmd);
DataTable dt = new DataTable();
adpt.Fill(dt);
transaction.Commit();
return dt;
} catch (Exception ex){
transaction.Rollback();
throw new Exception("DataQuery: " + ex.Message);
} finally {
FbConn.Close();
}
}
}
//NonQuery query:
public int NonQuery(string query){
if(!getState().Equals("Open")) FbConn.Open();
using (FbTransaction transaction = FbConn.BeginTransaction()) {
try{
FbCommand cmd = new FbCommand(query, FbConn, transaction);
int i = cmd.ExecuteNonQuery();
transaction.Commit();
return i;
} catch (Exception ex){
transaction.Rollback();
throw new Exception("NonQuery: " + ex.Message);
} finally {
FbConn.Close();
}
}
}
}
這是我使用課程的方式:
DataTable dt = fBird.Instance.qSelect("SELECT * FROM USERS");
int i = fBird.Instance.NonQuery("DELETE FROM TABLE");
這是計時器的使用方法:
public partial class MainForm : Form {
private System.Timers.Timer aTimer;
...
void setTimers() {
aTimer = new System.Timers.Timer(1500));
aTimer.Elapsed += OnTimedEvent;
aTimer.AutoReset = true;
aTimer.Enabled = true;
}
void OnTimedEvent(Object source, ElapsedEventArgs e) {
try{
//different changes to & reads from database
} catch(Exception ex) {
MessageBox.Show("OnTimedEvent: " + ex.Message);
}
}
}
我發現,由於某種原因,與Firebird的連接不止一個(單個子不應該允許,對吧?)。 我可以在Windows性能監視器的“網絡”選項卡上看到它。 作為測試,我執行非常不同的操作,在應用程序中打開不同的窗口和屏幕,還保存了一些更改。 隨機出現錯誤(並行事務),並創建了新的連接,並且一段時間后(例如30-40次單擊),一切正常。
這簡直讓我發瘋。 我希望你們中的某人能夠幫助我。 我感覺自己在這里做錯了什么,但是我找不到。 讓我知道您是否需要更多信息。
如果你是使用多線程,那么你不應該共享一個連接。 您的問題不是多個連接之一,而是多個使用同一連接並嘗試創建自己的事務的線程,而驅動程序顯然不支持這些事務。 您的代碼也可能會遭受其他競爭條件的困擾。
停止使用該單例。 而是為每個工作單元獲取一個新的連接,並在完成后關閉它。 默認情況下,Firebird ADO.net提供程序提供連接池,因此這樣做相對便宜。
就個人而言,我將完全擺脫該fBird
類,但否則至少擺脫該共享實例,實現IDisposable
以便在處置時關閉連接,添加靜態方法以根據需要創建實例。 然后,應在using
使用這些fBird
實例,以確保最后正確關閉它。
我建議擺脫該fBird
類的原因是,您只是包裝了普通的連接對象,並提供了較差且較脆的抽象。
您需要使用鎖,單例不能保證單個用戶
關於fBird類,您可能需要重新考慮三件事。
首先,正如其他答案所指出的那樣,FBConnection對象不是線程安全的。 (這是Firebird的設計/限制,而不是C#庫特定的行為)。 這意味着您可以:
其次,您的方法非常繁瑣,因為您每次都要進行完全連接/斷開連接。 當您調高音量時,這確實會減慢速度。
第三,您始終在NonQuery中進行提交。 如果您的操作涉及多個查詢,那么如果兩者之間都失敗,則數據庫可能會不一致。
這是我的前兩點的折衷解決方案。 在我的一個舊應用程序中(不對不起C#),我創建了一個連接代理,以減少靜態服務中的連接/斷開連接開銷。 這具有最大的並發連接配置值。
您編寫一個GetConnection和ReturnConnection方法。
GetConnection在確定現有連接是否可用時在內部鎖定。 然后將其標記為正在使用並返回。 如果沒有可用的連接,則它將建立一個新連接(延遲創建),將其標記為已使用並返回(前提是您處於最大限制之內)。
ReturnConnection內部鎖定,然后將其標記為可用。 如果可疑連接(例如,使用連接時發生套接字錯誤),則在返回狀態時有一個可選參數。 ReturnConnection要做的另一件事是給它加時間戳。 如果一段時間未使用連接,則后台進程會定期關閉超過一定數量的連接。
GetConnection保證事務未啟動。 如果它正在事務中(調用者必須提交,回滾或指示存在問題),或者連接已關閉,則ReturnConnection引發異常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.