7

可以停止正在运行的阅读器吗?

场景:我有一个包含 100000 个数据集的表

CREATE TABLE stock (
uid bigint NOT NULL,
name text,
quantity integer,
x bytea,
y bytea
);

和一个用于读取数据的控制台应用程序(.NET 4.0、Npgsql 2.0.11.0/2.0.11.92)

conn = new NpgsqlConnection("Server=localhost;Database=postgres;User id=postgres;password=postgres;Timeout=600;CommandTimeout=600;ConnectionLifeTime=600;");
using (new ConnectionOpen(conn))
using (var ta = conn.BeginTransaction(IsolationLevel.Snapshot))
{
    IDbCommand command = conn.CreateCommand("SELECT * from stock;");
    command.SetTransaction(ta);
    IDataReader reader = command.ExecuteReader();

    int n = 0;
    while (!reader.IsClosed && reader.Read())
    {
        n++;

        if (n > 5000)
        {
            if (reader != null)
            {
                 ((NpgsqlDataReader)reader).Close();
            }
        }
     }
     ((NpgsqlDataReader)reader).Dispose();
     reader = null;
}

我观察到数据阅读器无法真正停止。似乎数据读取器首先读取所有行,然后正常返回。

这个例子是一个更大的应用程序的摘要,其中用户将通过按下按钮来停止数据读取器,因为读取时间太长。

4

4 回答 4

9

我知道这个线程很老,但我相信这个人的问题的正确答案如下:

command.Cancel(); //execute before closing the reader
reader.Close();

通过调用 DbCommand.Cancel(),您表明不应再处理任何记录,并且命令(包括基础查询)应立即停止并快速退出。如果您不取消该命令,当您尝试关闭 DbDataReader(或跳出循环/使用块),并且您正在处理返回的大量记录时,Close() 方法将填充输出参数、返回值和 RecordsAffected 的值。

如果您在读取所有记录之前尝试关闭阅读器,Close 会尝试读取所有数据并填写这些值,并且它似乎只是挂起(很可能导致某种超时异常被抛出)。如果你不关心结果集中的剩余值——如果你要跳出读取循环,你很可能不关心——你应该在调用 Close() 之前取消底层命令。

以上信息部分检索自:https ://www.informit.com/guides/content.aspx?g=dotnet&seqNum=610

于 2014-08-07T21:17:57.383 回答
0

数据读取器通常从数据库服务器返回数据块(至少 SQL Server 是这样工作的)。Postgre SQL 在幕后可能会有不同的行为。

解决此问题的另一种方法是将数据加载作为后台任务(BackgroundWorkerTask等)。这样一来,您的 UI 线程就会保持响应状态,而阅读器在幕后的实现方式并不重要。

于 2012-01-25T16:19:45.417 回答
0

您可以在 while 循环中设置一个中断,但不确定是否/如何将其与用户操作联系起来,让他们决定何时中断读取循环。或者,您可以重组代码,使其返回前 x 行,然后给它们一个继续按钮以返回其余行或返回下 x 行。

于 2012-01-25T16:02:41.933 回答
0

这是一个猜测。

可能的解决方案 1

...
using (var ta = conn.BeginTransaction(IsolationLevel.Snapshot))
{
    IDbCommand command = conn.CreateCommand("SELECT * from stock;");
    command.SetTransaction(ta);
    IDataReader reader = command.ExecuteReader();

    int n = 5000;

    //put it in using
    using(IDataReader reader = command.ExecuteReader())
    {
        //read first N rows
        for(int i=0;i<n;i++)
        {           
            //get value from the columns of the current row
            for (i = 0; i < reader.FieldCount; i++)
            {
                Console.Write("{0} \t", reader[i]);
            }
        }
    }    
}

可能的解决方案 2

使用TOPsql 命令,查看示例

于 2012-01-25T16:07:48.660 回答