2

在以下代码片段中,AddRow()从非 UI 线程调用:

  public partial class Form1 : Form
  {
    public delegate void InvokeDelegate();

    ...
    SqlConnection mSqlConnection = new SqlConnection("Data Source=" + Environment.MachineName + "\\SQLEXPRESS; Initial Catalog=orderDB; Integrated Security=TRUE; MultipleActiveResultSets=True;");

    DataSet mDataSet = new DataSet();
    SqlDataAdapter mSqlDataAdapter = new SqlDataAdapter();


    ...
    private void UpdateGridView()
    {
      if (mSqlConnection.State == ConnectionState.Closed)
        mSqlConnection.Open();

      mSqlDataAdapter.SelectCommand = new SqlCommand("SELECT * FROM customerTable", mSqlConnection);
      mDataSet.Clear();
      mSqlDataAdapter.Fill(mDataSet);
      dataGridView1.DataSource = mDataSet.Tables[0];

      if (mSqlConnection.State == ConnectionState.Open)
        mSqlConnection.Close();
    }


    public void AddRow(int field1, int field2, int field3)
    {
      mSqlDataAdapter.InsertCommand = new SqlCommand("INSERT INTO customerTable VALUES(@field1, @field2, @field3)", mSqlConnection);

      mSqlDataAdapter.InsertCommand.Parameters.Add("@field1", SqlDbType.Int).Value = field1;
      mSqlDataAdapter.InsertCommand.Parameters.Add("@field2", SqlDbType.Int).Value = field2;
      mSqlDataAdapter.InsertCommand.Parameters.Add("@field3", SqlDbType.Int).Value = field3;

      mSqlConnection.Open();
      mSqlDataAdapter.InsertCommand.ExecuteNonQuery();
      dataGridView1.BeginInvoke(new InvokeDelegate(UpdateGridView)); // UpdateGridView() won't work from a non-UI thread
      mSqlConnection.Close();

    }
}

在不得不AddRow()从非 UI 线程调用之前,我UpdateGridView()被直接调用并且它完美地工作。但是现在AddRow()不再保证从 UI 线程调用,所以我将直接调用替换为dataGridView1.BeginInvoke().

一旦我这样做了,我的基于表单的应用程序开始System.InvalidOperationException每隔几次AddRow()调用就抛出一个,中断mSqlDataAdapter.Fill(mDataSet);语句()并带有以下消息:

阅读器关闭时调用 Read 的尝试无效

我的问题是为什么?

  1. 什么读者?数据适配器的?SqlConnection 的?DataGridView 的数据源?
  2. 我正在处理BeginInvoke()mSqlConnection 的Open()Close()周围,如果 mSqlConnection 没有打开,我什至会打开它(再次!),那么我为什么会收到这个“关闭”错误?
  3. 解决这个问题的正确方法是什么?(即从非 UI 线程更新 DataGridView)
4

2 回答 2

2

这个问题肯定是由于比赛条件。

从 UpdateGridView 中删除这两行,因为它不是关闭连接的正确位置。

 if (mSqlConnection.State == ConnectionState.Open)
    mSqlConnection.Close();

使用IAsyncResult检索等待句柄并等待线程完成 GridUpdate。

 IAsyncResult Result = dataGridView1.BeginInvoke(new InvokeDelegate(UpdateGridView));
 Result.AsyncWaitHandle.WaitOne();
 mSqlConnection.Close();
于 2012-10-26T22:08:35.903 回答
0

我认为您有种族问题,请在您的线程中查看这部分代码:

 dataGridView1.BeginInvoke(new InvokeDelegate(UpdateGridView)); 
 mConnection.Close();

BeginInvoke 将在这样的事情方便控制时运行委托。因此,您从线程中调用它,它会启动您在 UI 线程中的填充,该线程可以持续很长时间(您正在从表中获取所有内容),并且在它获取数据时,调用关闭单独线程中的连接会关闭它。你提到它只是有时发生,在大多数情况下,线程会关闭连接,你会在UpdateGridView()方法中重新打开它,但有时,你会在线程关闭连接之前开始填充数据集。您应该同步打开和关闭连接。

切换这两个应该可以解决您的问题,但您每次都会建立两个连接:

mConnection.Close();
dataGridView1.BeginInvoke(new InvokeDelegate(UpdateGridView));
于 2012-10-26T21:45:22.537 回答