C# 使用ExecuteOnQuery的并发问题

C# 使用ExecuteOnQuery的并发问题,c#,sql,logging,time-precision,C#,Sql,Logging,Time Precision,我正在将简单的文本日志条目写入SQL表,如下所示: private SqlConnection sqlcon; // ... try { sqlcon.Open(); SqlCommand cmd = sqlcon.CreateCommand(); cmd.CommandText = this.logString; cmd.ExecuteNonQuery(); } /* catch SqlException, InvalidOperationExcept

我正在将简单的文本日志条目写入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个或更多日志条目,则情况并非如此

所以我猜ExecuteOnQuery运行在不同的线程上,不同的事件不知怎么搞混了。我已经尝试过将异步处理与BeginExecutenQuery和EndExecutenQuery一起使用,但它弄乱了我的代码,而且根本无法工作

所以我的问题是:有没有办法确保非查询的执行顺序与它们被触发的顺序完全一致

编辑: 也许这很重要,我用的是这样的时间戳

cmd.Parameters.Add("timestamp", SqlDbType.DateTime ).Value = System.DateTime.Now;
异步的? 如果你每次都打开/关闭连接,你不能确定,那么调用
BeginExecutenQuery
(这是在MARS之前使用的方法)在某种程度上是等效的(请原谅我,我知道这并不准确)。实际上,每个请求都是同步的,但是,如果以5毫秒的间隔记录某个内容,则无法确定SQL Server将为该请求提供服务的顺序(这里的DB人员会考虑标识列的老问题)

时间戳 然后,解决方案应该是使用时间戳列,并基于该列对结果进行排序(写入顺序无关紧要)。这是一个完美的解决方案(正如其他答案所指出的,不要忘记使用正确的数据类型来存储数据)

如何获得实际时间?您可以使用
DateTime.Now
函数在客户端(如您的示例中)执行此操作,或者在服务器端使用
SYSDATETIME
(如@PetrAbdulin所指出的)。如果您不需要高分辨率,这是一个完美的解决方案

精度

这两个函数都依赖于Windows系统计时器,并且它的分辨率不允许超过10毫秒(那么,如果您确实需要5毫秒的粒度,您应该避免使用它们)

在MSDN上,您可以看到Windows NT上的分辨率为10毫秒,调用,
FILETIME
结构的精度为100毫秒,但是计时器本身不能达到该结果!!!在某些情况下,您甚至可能得到1毫秒的滴答声,但它根本不可靠。在有关秒表计时器的文档中,您可以阅读:

秒表类使用的计时器取决于系统硬件 和操作系统。如果秒表计时器 基于高分辨率性能计数器。否则,, IsHighResolution为false,表示秒表计时器为 基于系统定时器

这是什么意思?系统计时器从未被授予高分辨率(不要被用于存储时间的结构的精度所迷惑)

这可能是个问题,也可能不是,这取决于您将如何使用日志。如果在备份过程中必须记录从一个文件夹复制到另一个文件夹的文件列表,则可能不需要这样的精度。如果您的日志可能用于法律事务(是的,我知道它们不应该有任何法律价值)或调试微妙的线程问题,那么您将需要它

解决方案

如果你需要一个高分辨率定时器(并且,在Windows上,你可以考虑任何10毫秒的高分辨率),你必须处理性能计数器。看一看。当然,你不需要所有这些东西,但它指出了问题所在

在.NET中,您可以使用带有秒表的
DateTime.Now
(检查
IsHighResolution
属性)。了解如何使用秒表来提高日期时间的精度。现在就开始

常见错误

首先,不要将用于存储时间的数据类型的精度与计时器的分辨率混淆。如果在低分辨率字段中存储该值,则计时器的精确程度无关紧要,但使用高分辨率字段不会将粗略计时器转换为高分辨率计时器

此外,您不应该使用本地时间作为时间戳,当系统时间因夏令时而改变时,您会混淆日志。想想这个:

00:00 02:00 02:01 03:00 02:00 Log #1 #2 #3 #4 System time -1 00:00 02:00 02:01 03:00 02:00 日志#1#2#3#4 系统时间-1 现在,当你阅读日志时,你会得到以下顺序:1,2,4,3。因此,您应该永远不要使用
DateTime。现在对于日志,您应该始终选择
DateTime.UtcNow
SYSUTCDATETIME
函数(而且
DateTime.UtcNow
的性能稍微好一点)。

异步? 如果你每次都打开/关闭连接,你不能确定,那么调用
BeginExecutenQuery
(这是在MARS之前使用的方法)在某种程度上是等效的(请原谅我,我知道这并不准确)。实际上,每个请求都是同步的,但是,如果以5毫秒的间隔记录某个内容,则无法确定SQL Server将为该请求提供服务的顺序(这里的DB人员会考虑标识列的老问题)

时间戳 然后,解决方案应该是使用时间戳列,并基于该列对结果进行排序(写入顺序无关紧要)。这是一个完美的解决方案(正如其他答案所指出的,不要忘记使用正确的数据类型来存储数据)

如何获得实际时间?您可以使用
DateTime.Now
函数在客户端(如您的示例中)执行此操作,或者在服务器端使用
SYSDATETIME
(如@PetrAbdulin所指出的)。如果您不需要高分辨率,这是一个完美的解决方案

精度

这两个函数都依赖于Windows系统计时器,并且它的分辨率不被授予mor