我知道您可以在不使用 Invoke/BeginInvoke 的情况下从工作线程读取 gui 控件,因为我的应用程序现在正在执行此操作。没有抛出跨线程异常错误,并且我的 System.Timers.Timer 线程能够很好地读取 gui 控件值(不像这个人:工作线程可以读取 GUI 中的控件吗?)
问题 1: 鉴于线程的基本规则,我应该使用 Invoke/BeginInvoke 来读取表单控制值吗?这是否使它更加线程安全?这个问题的背景源于我的应用程序遇到的问题。似乎随机破坏了另一个线程正在引用的表单控件。(见问题 2)
问题 2: 我有第二个线程需要更新表单控件值,所以我调用/BeginInvoke 来更新这些值。好吧,同一个线程需要对这些控件的引用,以便它可以更新它们。它包含这些控件的列表(比如 DataGridViewRow 对象)。有时(并非总是),DataGridViewRow 引用会“损坏”。我所说的损坏是指引用仍然有效,但某些 DataGridViewRow 属性为空(例如:row.Cells)。这是由问题 1 引起的,还是您能给我一些提示,说明为什么会发生这种情况?
这是一些代码(最后一行有问题):
public partial class MyForm : Form
{
void Timer_Elapsed(object sender)
{
// we're on a new thread (this function gets called every few seconds)
UpdateUiHelper updateUiHelper = new UpdateUiHelper(this);
// Is it thread-safe to step through the datagrid rows here without invoking?
foreach (DataGridViewRow row in dataGridView1.Rows)
{
object[] values = GetValuesFromDb();
updateUiHelper.UpdateRowValues(row, values[0]);
}
// .. do other work here
updateUiHelper.UpdateUi();
}
}
public class UpdateUiHelper
{
private readonly Form _form;
private Dictionary<DataGridViewRow, object> _rows;
private delegate void RowDelegate(DataGridViewRow row);
private readonly object _lockObject = new object();
public UpdateUiHelper(Form form)
{
_form = form;
_rows = new Dictionary<DataGridViewRow, object>();
}
public void UpdateRowValues(DataGridViewRow row, object value)
{
if (_rows.ContainsKey(row))
_rows[row] = value;
else
{
lock (_lockObject)
{
_rows.Add(row, value);
}
}
}
public void UpdateUi()
{
foreach (DataGridViewRow row in _rows.Keys)
{
SetRowValueThreadSafe(row);
}
}
private void SetRowValueThreadSafe(DataGridViewRow row)
{
if (_form.InvokeRequired)
{
_form.Invoke(new RowDelegate(SetRowValueThreadSafe), new object[] { row });
return;
}
// now we're on the UI thread
object newValue = _rows[row];
row.Cells[0].Value = newValue; // randomly errors here with NullReferenceException, but row is never null!
}