3 Stimmen

Ist es Thread sicher zu lesen, ein Formular Steuerelemente Wert (aber nicht ändern) ohne Invoke/BeginInvoke von einem anderen Thread verwenden

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!
    }

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X