1

我正在使用 WinForms 在 C# 中的 Visual Studio 2010 .NET 4.0 中工作。

该表单有一个DataGridView数据绑定到DataSet.

DataSet正在从Thread处理从 读取的数据的中填充ConcurrentQueue

该代码还使用信号量来序列化对 的访问,DataSet因为它可以从“工作”线程和 UI 主线程(更新时DataGridView)访问。

UI/Form 有一个System.Windows.Forms.Timer每 1/4 秒触发一次以调用一个函数,该函数DataGridView导致DataSet.

代码正确运行,直到DataGridView数据滚动并且滚动条变得可见为止。此时整个表单变得无响应 - 标题标题为“程序无响应”。

有趣的是,在调试器下运行时不会发生这种情况。仅在部署程序时。并且从未提出过任何例外。

我已经尝试同时使用 Invoke 和 BeginInvoke 而行为没有改变。

问题:

1-我下面的代码可能有什么问题?
2-由于在调试器下运行时无法观察到这种行为,我该如何找出导致问题的原因?

下面提供了代码墙。我知道这是很多代码,但我尽可能多地删减。

// deleted using statements for brevity

namespace namcom
{
    public partial class wndXMLtrans : Form
    {
        private DataSet dsRecords = new DataSet();
        private Thread threadConsumeXML = null;
        private static Semaphore ResourceLock;

        public wndXMLtrans(String ip)
        {
            InitializeComponent();
            ResourceLock = new Semaphore(1, 1);
            curSize = this.Size;
            m_ip = ip;
        }

        private Boolean AddRowToDataSet(String[] columns, String xml)
        {
            Boolean retCode = true;
            String value = String.Empty;
            Int64 id = -1;
            DataRow row;

            ResourceLock.WaitOne();
            row = dsRecords.Tables[0].NewRow();

            // prepare row code omitted - brevity

            // add new data row to DataSet
            dsRecords.Tables[0].Rows.Add(row);
            ResourceLock.Release();
            // SQL inserts into DB removed - brevity
            return (retCode);
        }

        private Boolean HandleSingleXMLMessage(String[] columns, String xml, out String exceptionMessage)
        {
            Boolean boolRet = true;
            exceptionMessage = String.Empty;

            // store data in dataset and database
            if ( closeRequested == false  )
            {
                AddRowToDataSet(columns, xml);
            }
            return (boolRet);
        }

        private Boolean StoreG2SMessages(String message)
        {
            // code removed - brevity
            // removed code just parses out string and in a loop calls HandleSingleXMLMessage
            // until all XML in message have been processed
            HandleSingleXMLMessage(columns,xml, out exceptionMessage)  // call in loop
            return (ret);
        }

        // pull XML out of mainwnd.msgQueue and update database
        private void G2SParseThread()
        {
            String Data;
            String exceptionMsg = String.Empty;

            while ( /* thread is to be active - code removed for brevity */)
            {
                Data = String.Empty;
                if (mainwnd.msgQueue.TryDequeue(out Data) == true)
                {
                    this.StoreG2SMessages(Data);
                }
                Thread.Sleep(20);
            }
            // thread ended cleanup code removed - brevity
        }


        private void StartThreads()
        {
            // start XML packet processing thread
            threadConsumeXML = new Thread(G2SParseThread);
            threadConsumeXML.SetApartmentState(ApartmentState.STA);
            threadConsumeXML.Start();

            threadMonitor = new Thread(() => threadHandleStatusMsg(ref mainwnd.statusQueue));
            threadMonitor.Start();
        }


        private void wndXMLtrans_Shown(object sender, EventArgs e)
        {
            // remove SQL code - brevity
            // fill dsRecords ( DataSet )
            try
            {
                var adapter = new SQLiteDataAdapter(selectSQL, dbConnection);
                adapter.Fill(dsRecords);
                dataGrid.DataSource = dsRecords.Tables[0].DefaultView;
                adapter.Dispose();
            }
            catch { } // catch code removed - brevity
            StartThreads();
        }

        private void gridUpdateTimer_Tick(object sender, EventArgs e)
        {
            ResourceLock.WaitOne();
            try
            {
                if (dataGrid != null && numRows < dsRecords.Tables[0].Rows.Count)
                {
                    if (dataGrid.RowCount > 0)
                    {
                        dataGrid.FirstDisplayedScrollingRowIndex = dataGrid.RowCount - 1;
                    }
                    dataGrid.Refresh();
                    numRows = dsRecords.Tables[0].Rows.Count;
                }
            }
            catch { }
            ResourceLock.Release();
        }
    }
}

编辑:汉斯提供的答案是您无法DataSet从工作线程更新绑定。所以我能够通过添加以下内容来解决问题:

这些由使用 Invoke 的委托函数调用。Unbind 在DataSet更新前调用,Bind afterDataSet被更新。这可行,但会导致DataGridView出现多次闪烁或重绘。有什么办法可以补救吗?

public void UnbindDataGridView(DataGridView grid)
{
    grid.DataSource = null;
}

public void BindDataGridView(DataGridView grid)
{
    grid.DataSource = dsRecords.Tables[0].DefaultView;
}
4

0 回答 0