我正在使用 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;
}