簡體   English   中英

使用 ExecuteNonQuery 的並發問題

[英]Concurrency-Issue using ExecuteNonQuery

我正在將簡單的文本日志條目寫入 SQL 表,如下所示:

private SqlConnection sqlcon;

// ...

try {
    sqlcon.Open();

    SqlCommand cmd = sqlcon.CreateCommand();
    cmd.CommandText = this.logString;

    cmd.ExecuteNonQuery();
} 
/* catch SqlException, InvalidOperationException, Exception */ 
/* finally sqlcon.Close() */

異步處理設置為 false,所以我認為日志條目會按執行順序出現在表中。 但情況並非如此,如果在大約不到 5 毫秒的時間內觸發了 2 個或更多日志條目。

所以我的猜測是 ExecuteNonQuery 在不同的線程上運行,並且不同的事件以某種方式混淆了。 我已經嘗試將異步處理與 BeginExecuteNonQuery 和 EndExecuteNonQuery 結合使用,但它弄亂了我的代碼並且無論如何都無法正常工作。

所以我的問題是:有沒有什么辦法可以確保nonQueries 完全按照它們被觸發的順序執行?

編輯:也許這很重要,我正在使用這樣的時間戳

cmd.Parameters.Add("timestamp", SqlDbType.DateTime ).Value = System.DateTime.Now;

異步?

如果您每次都打開/關閉連接,您不能真正確定這一點,那么調用BeginExecuteNonQuery (這是 MARS 之前使用的方法)在某種程度上等同於(請原諒我,我知道這並不准確)。 實際上每個請求都是同步的,但是,如果您以 5 毫秒的間隔記錄某些內容,則您無法確定 SQL 服務器將為該請求提供服務的順序(這里的數據庫人員會考慮 IDENTITY 列的問題)。

時間戳

那么解決方案應該是使用 TimeStamp 列並根據該列對結果進行排序(寫入順序無關緊要)。 這是完美的解決方案(正如其他答案所指出的那樣,不要忘記使用正確的數據類型來存儲數據)。

如何獲得實際時間? 您可以在客戶端(如您的示例中)使用DateTime.Now function或在服務器端使用SYSDATETIME (如@PetrAbdulin 所指出)。 如果您不需要高分辨率,這是一個完美的解決方案。

精確

這兩個函數都依賴於 Windows 系統定時器,並且它的分辨率不超過 10 毫秒(然后,如果你真的需要 5 毫秒的粒度,你應該避免它們)。

在 MSDN 上,您可以閱讀 Windows NT 上的DateTime.Now的分辨率為 10 毫秒, SYSDATETIME調用GetSystemTimeAsFileTimeFILETIME結構具有 100 毫秒的精度,但定時器本身未被授予實現該結果 在某些情況下,您甚至可能會得到 1 毫秒的滴答聲,但它根本不可靠 在有關 StopWatch 計時器的文檔中,您可以閱讀:

秒表 class 使用的計時器取決於系統硬件和操作系統。 如果秒表計時器基於高分辨率性能計數器,則 IsHighResolution 為真。 否則,IsHighResolution 為 false,表示 Stopwatch 計時器基於系統計時器。

這是什么意思? 系統計時器永遠不會被授予高分辨率(不要被用於存儲時間的結構的精度所混淆)。

這可能是一個問題,這取決於您將使用日志的內容 如果您必須在備份期間記錄從一個文件夾復制到另一個文件夾的文件列表,那么您可能不需要這樣的精度。 如果您的日志可能用於合法用途(是的,我知道它們不應該具有任何法律價值)或用於調試微妙的線程問題,那么您將需要它。

解決方案

如果您需要高分辨率計時器(並且,在 Windows 上,您可以將小於 10 毫秒的任何東西視為高分辨率),您必須處理性能計數器。 看看這篇關於計時的精彩文章 當然,您不需要所有這些東西,但它指出了問題所在。

在 .NET 中,您可以將DateTime.NowIsHighResolution StopWatch )。 在 SO 上閱讀這篇關於使用StopWatch提高DateTime.Now精度的好文章。

常見錯誤

首先不要混淆用於存儲時間的數據類型的精度與計時器的分辨率。 如果您將該值存儲在低分辨率字段中,但使用高分辨率字段不會將您的粗略計時器轉換為高分辨率,那么您的計時器的精確度並不重要。

此外,你不應該使用本地時間作為時間戳,當系統時間因夏令時而改變時,你會混淆你的日志。 想一想:

               00:00   02:00   02:01   03:00   02:00
Log              #1      #2      #3              #4
System time                             -1

現在你什么時候閱讀你的日志,你會得到這個順序:1、2、4、3。因此,你永遠不應該為你的日志使用DateTime.NowSYSDATETIME ,你應該總是更喜歡DateTime.UtcNowSYSUTCDATETIME函數(而且DateTime.UtcNow的性能要好一些)。

如果使用 SQL Server SYSDATETIME插入時間戳怎么辦? 我認為ExecuteNonQuery是同步的,但您的時間戳可能有問題。

Windows 中有一篇關於時間分辨率的好文檔。

SQL 服務器的日期時間精度為 3 毫秒。 那些亂序列出的條目是否具有相同的時間戳? (毫秒以 0、3 或 7 結尾)。

您需要一個精度更高的字段,例如datetime2數據類型(可從 SQL Server 2008 獲得),或用於正確排序項目的增量計數器。

你為什么不使用交易? 如果數據很重要,那么您應該使用 SqlTransaction。

為什么需要訂購? 你的桌子上有pk嗎? 它是如何插入的,或者是你的 pk 的時間戳?

順便說一句,請不要緩存連接。 .net 正在為您做的更好。 在此 msdn 示例中使用 using like 。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM