首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ArgumentOutOfRangeException in SqlCommand

ArgumentOutOfRangeException in SqlCommand
EN

Stack Overflow用户
提问于 2015-03-13 08:55:38
回答 1查看 490关注 0票数 1

我有两个定时器。其中一个计时器从plc检索数据并更新datatable中的相关数据行。在另一个计时器中,我将datatable作为参数发送到存储过程。问题是,有时我的sqlCommand.ExecuteNonQuery()会给我一个ArgumentOutOfRangeException。数据表中有128行。我从plc中读取了512字节。一行表示一个浮点数(这意味着4个字节)

我无法理解异常ArgumentOutOfRange。变量计数与行计数相匹配。有什么问题吗。为什么我有时会犯这样的错误呢?

这是我的密码

代码语言:javascript
复制
        void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            timer1.Stop();
            byte[] data = new byte[512];
            int res = dc.readManyBytes(libnodave.daveDB, 19, 0, 512, data);
            if (res == 0)
            {
                for (int i = 0; i < 128; i++)
                {
                    byte[] temp = new byte[] { data[(i * 4 + 3)], data[(i * 4 + 2)], data[(i * 4 + 1)], data[(i * 4)] };
                    double value = Math.Truncate(Convert.ToDouble(BitConverter.ToSingle(temp, 0)) * 100) / 100;
                    DataRow row = dtAddress.Rows[i];
                    switch (row["DataType"].ToString())
                    {
                        case "REAL":
                            DataRow[] rValues = dtValue.Select("AddressID = " + row["ID"]);
                                foreach (DataRow rValue in rValues)
                                {
                                    rValue["Value"] = value;
                                    rValue["LastUpdate"] = DateTime.Now;
                                }
                            break;
                    }
                }
            }
       }

    void timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        using (SqlCommand crudValues = new SqlCommand("dbo.crudValues", connection))
        {
            crudValues.CommandType = CommandType.StoredProcedure;
            SqlParameter param = crudValues.Parameters.AddWithValue("@tblValue", dtValue);
            param.SqlDbType = SqlDbType.Structured;

            crudValues.ExecuteNonQuery();
        }
    }

-SQL存储过程

代码语言:javascript
复制
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[crudValues]
    @tblValue as dbo.tblValue READONLY
AS
BEGIN
    SET NOCOUNT ON;
    UPDATE tblValue SET tblValue.Value = t.Value, tblValue.LastUpdate = t.LastUpdate FROM tblValue INNER JOIN @tblValue t ON tblValue.ID = t.ID
END

叠迹;

代码语言:javascript
复制
   at System.Data.SqlClient.TdsParser.TdsExecuteRPC(_SqlRPC[] rpcArray, Int32 timeout, Boolean inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, Boolean isCommandProc, Boolean sync, TaskCompletionSource`1 completion, Int32 startRpc, Int32 startParam)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at GazMotoruPLCScanner.Program.timer2_Elapsed(Object sender, ElapsedEventArgs e) in d:\Projeler\TRES ENERJİ\GazMotoruPLCScanner\Program.cs:line 106
   at System.Timers.Timer.MyTimerCallback(Object state)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-03-17 05:34:39

如果问题确实是由两个线程同时处理同一个DataTable对象造成的,那么一个可能的解决方案是使用互斥同步两个线程。

当两个或多个线程需要同时访问共享资源时,系统需要一种同步机制,以确保每次只使用一个线程。Mutex是一个同步原语,它只授予一个线程对共享资源的独占访问权。如果线程获得互斥,则希望获取互斥的第二个线程被挂起,直到第一个线程释放互斥。

在您的示例中,第一个事件处理程序向DataTable添加元素,第二个事件处理程序将此DataTable发送到存储过程。如果在RunExecuteReader尝试从其中读取行时更改了该对象,那么任何事情都可能发生。

创建可以从timer1_Elapsed()timer2_Elapsed()访问的Mutex类的一个实例。

代码语言:javascript
复制
private static Mutex mut = new Mutex();

您的计时器事件处理程序可能如下所示:

代码语言:javascript
复制
void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    int iMaxWaitMSec = 10000;
    if (mut.WaitOne(iMaxWaitMSec))
    {
        try
        {
            // Populate DataTable
        }
        catch
        {
        }
        finally
        {
            mut.ReleaseMutex();
        }
    }
    else
    {
        // we waited longer than iMaxWaitMSec milliseconds
        // in an attempt to lock the mutex
        // skip this timer event
        // we'll retry next time
    }
}

代码语言:javascript
复制
void timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    int iMaxWaitMSec = 10000;
    if (mut.WaitOne(iMaxWaitMSec))
    {
        try
        {
            // Send DataTable to the database
        }
        catch
        {
        }
        finally
        {
            mut.ReleaseMutex();
        }
    }
    else
    {
        // we waited longer than iMaxWaitMSec milliseconds
        // in an attempt to lock the mutex
        // skip this timer event
        // we'll retry next time
    }
}

检查语法错误。将超时设置为适当的值。当获取互斥体所需的时间过长时,添加适当的情况处理。

这种方法的结果是,timer1_Elapsed()timer2_Elapsed()中的两个代码块在if (mut.WaitOne(iMaxWaitMSec))中永远不会同时运行。

如果您有一些不接触共享DataTable的额外代码,并且不希望该代码被阻塞,等待第二个事件处理程序,则可以将其放在if (mut.WaitOne(iMaxWaitMSec))块之外。

更新

根据你的评论,以下是我对如何安排整个节目的想法。

主要目标是尽量减少两个线程可能等待对方的时间。

1)确保使用多线程计时器:System.Timers.TimerSystem.Threading.Timer,而不是System.Windows.Forms.Timerhttps://msdn.microsoft.com/en-us/library/system.timers.timer(v=vs.110).aspx

我希望timer事件处理程序在一个单独的线程上运行。

如果对经过的事件的处理持续时间超过间隔,则可能在另一个ThreadPool线程上再次引发该事件。

因此,有一个标志,指示正在处理的事件,并检查它。我不认为您想再次调用您的存储过程,而之前调用它的尝试还没有完成。

2)在内存中有一个可以用数据容纳队列的结构。第一个定时器会定期从PLC读取数据,并将数据追加到队列的末尾。第二个定时器将定期检查队列并从队列的开头选择挂起的数据。有一个类Queue。理想情况下,它应该能够快速地将元素追加到其末尾,并从一开始就快速删除元素。在.NET 4中有ConcurrentQueue,这意味着您不需要显式的Mutexe。

如果将数据插入数据库突然变得缓慢(即网络关闭),队列将增长并包含多个元素。在这种情况下,应该由您来决定要做什么--丢弃额外的元素,或者仍然尝试插入所有这些元素。

3)应该只使用Mutex来防止对这个“队列”对象的同时访问,以减少等待。

代码语言:javascript
复制
// somewhere in the main program
Queue<DataTable> MainQueue = new Queue<DataTable>();
// or in .NET 4
ConcurrentQueue<DataTable> MainConcurrentQueue = new ConcurrentQueue<DataTable>();

..。

代码语言:javascript
复制
void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // read data from PLC
    // parse, process the data
    // create a **new** instance of the DataTable object
    DataTable dt = new DataTable();
    // and fill it with your data

    // append the new DataTable object to the queue
    mut.WaitOne();
    try
    {
        MainQueue.Enqueue(dt);
    }
    catch { }
    finally
    {
        mut.ReleaseMutex();
    }

    // or in .NET4 simply
    MainConcurrentQueue.Enqueue(dt);
}

..。

代码语言:javascript
复制
void timer2_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    DataTable dt = null;

    mut.WaitOne();
    try
    {
        dt = MainQueue.Dequeue();
    }
    catch { }
    finally
    {
        mut.ReleaseMutex();
    }

    // or in .NET4 simply
    dt = MainConcurrentQueue.Dequeue();

    // Send DataTable to the database

    // TODO: add checks for empty queue
    // TODO: add checks for long queue 
    // and send all or some of the accumulated elements to the DB
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29028208

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档