Is it thread safe to read a form controls value (but not change it) without using Invoke/BeginInvoke from another thread

Posted by goku_da_master on Stack Overflow See other posts from Stack Overflow or by goku_da_master
Published on 2012-09-06T21:13:41Z Indexed on 2012/09/06 21:38 UTC
Read the original article Hit count: 141

Filed under:
|
|

I know you can read a gui control from a worker thread without using Invoke/BeginInvoke because my app is doing it now. The cross thread exception error is not being thrown and my System.Timers.Timer thread is able to read gui control values just fine (unlike this guy: can a worker thread read a control in the GUI?)

Question 1: Given the cardinal rule of threads, should I be using Invoke/BeginInvoke to read form control values? And does this make it more thread-safe? The background to this question stems from a problem my app is having. It seems to randomly corrupt form controls another thread is referencing. (see question 2)

Question 2: I have a second thread that needs to update form control values so I Invoke/BeginInvoke to update those values. Well this same thread needs a reference to those controls so it can update them. It holds a list of these controls (say DataGridViewRow objects). Sometimes (not always), the DataGridViewRow reference gets "corrupt". What I mean by corrupt, is the reference is still valid, but some of the DataGridViewRow properties are null (ex: row.Cells). Is this caused by question 1 or can you give me any tips on why this might be happening?

Here's some code (the last line has the 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);       

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

© Stack Overflow or respective owner

Related posts about .NET

Related posts about multithreading