Ich weiß, dass Sie ein Gui-Steuerelement von einem Worker-Thread lesen können, ohne Invoke/BeginInvoke zu verwenden, weil meine App es jetzt tut. Der Cross-Thread-Ausnahmefehler wird nicht ausgelöst und mein System.Timers.Timer-Thread ist in der Lage, Gui-Steuerungswerte zu lesen (im Gegensatz zu diesem Kerl: Kann ein Worker-Thread ein Steuerelement in der GUI lesen? )
Frage 1: Angesichts der Kardinalregel von Threads, sollte ich Invoke/BeginInvoke verwenden, um Formularsteuerungswerte zu lesen? Und macht dies es mehr Thread-sicher? Der Hintergrund dieser Frage ergibt sich aus einem Problem, das meine Anwendung hat. Sie scheint zufällig Formularsteuerelemente zu beschädigen, auf die ein anderer Thread verweist. (siehe Frage 2)
Frage 2: Ich habe einen zweiten Thread, der Formularsteuerungswerte aktualisieren muss, also rufe ich Invoke/BeginInvoke auf, um diese Werte zu aktualisieren. Dieser Thread benötigt einen Verweis auf diese Steuerelemente, damit er sie aktualisieren kann. Er enthält eine Liste dieser Steuerelemente (sagen wir DataGridViewRow-Objekte). Manchmal (nicht immer) wird die DataGridViewRow-Referenz "beschädigt". Was ich mit beschädigt meine, ist, dass der Verweis noch gültig ist, aber einige der DataGridViewRow-Eigenschaften null sind (z. B. row.Cells). Ist dies durch Frage 1 verursacht oder können Sie mir Tipps geben, warum dies passieren könnte?
Hier ist etwas Code (die letzte Zeile hat das Problem):
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!
}