私のアプリは現在それを行っているため、Invoke/BeginInvoke を使用せずにワーカー スレッドから GUI コントロールを読み取ることができることはわかっています。クロス スレッド例外エラーはスローされず、私の System.Timers.Timer スレッドは GUI コントロール値を正常に読み取ることができます (この男とは異なり:ワーカー スレッドは GUI でコントロールを読み取ることができますか? )
質問 1: スレッドの基本ルールを考えると、Invoke/BeginInvoke を使用してフォーム コントロールの値を読み取る必要がありますか? そして、これによりスレッドセーフになりますか? この質問の背景は、私のアプリが抱えている問題に由来しています。別のスレッドが参照しているフォーム コントロールがランダムに破損しているようです。(質問 2 を参照)
質問 2: フォーム コントロールの値を更新する必要がある 2 番目のスレッドがあるため、これらの値を更新するために Invoke/BeginInvoke を実行します。この同じスレッドは、それらのコントロールを更新できるように、これらのコントロールへの参照を必要とします。これらのコントロールのリストを保持します (DataGridViewRow オブジェクトなど)。場合によっては (常にではありません)、DataGridViewRow 参照が「破損」することがあります。破損とは、参照はまだ有効ですが、DataGridViewRow プロパティの一部が null (例: 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!
}