0

I have a background thread doing some work and a UI displaying the progress, and for various reasons, I'm not using a background worker; instead, a Timer triggers updates to the UI.

I'm not using Invoke calls at all. Instead, the background process writes to an array of 4 strings. This array is declared as an instance member of my main form.

Do I need to use locks to read this array from the UI thread? Is it fine to write to the array from the background thread and read from it from the UI one without any extra precautions?

EDIT: MSDN reads "The lock keyword marks a statement block as a critical section by obtaining the mutual-exclusion lock for a given object, executing a statement, and then releasing the lock.". Doesn't that mean that the lock will only prevent the same block of code from being run by two different threads?

Oscar Mederos
  • 29,016
  • 22
  • 84
  • 124
Clément
  • 12,299
  • 15
  • 75
  • 115
  • 1
    Clément by the way, why aren't you using Invoke calls? I think this is pretty easy, and you won't need to store anything in an array or do some tweaks (just in case you're storing info on the array to show on the UI by the main thread). – Oscar Mederos Apr 28 '11 at 08:01
  • @Oscar: Mono doesn't support them correctly on Windows. – Clément Apr 28 '11 at 08:14
  • Ok. See my last comment under my answer. – Oscar Mederos Apr 28 '11 at 08:17
  • Which `Timer` are you using? There are 3 in .Net, and one of them - `System.Windows.Forms.Timer` - executes on your message loop thread, thus does not require any synchronization code. – skolima Apr 28 '11 at 10:12
  • @Skolima: That's the one I'm using; only, the background job runs on a background thread, hence the problem. – Clément Apr 28 '11 at 12:56

3 Answers3

1

The quick answer is that you need to use locks if you have multiple threads that are writing to your array of strings at the same time. Here is a link to the latest reference on locks for c# http://msdn.microsoft.com/en-us/library/c5kehkcz%28v=VS.100%29.aspx.

ljkyser
  • 999
  • 5
  • 9
  • But no locks for writing from one and reading from the other, all right? – Clément Apr 28 '11 at 07:51
  • 1
    As Oscar Mederos notes, you should lock in both places to avoid the case that you are reading the values as they are being changed by the writing thread, you will end up with data inconsistency. It also covers you if you ever end up with multiple threads writing. – ljkyser Apr 28 '11 at 08:03
  • 1
    @Clément: No, reading and writing do not need to be in the same function. You would lock on the array when reading (wherever you have that code - looks like the timer fired event in this case) and you would lock on the array when writing (in the other code section). – ljkyser Apr 28 '11 at 08:24
1

Have you thought about what will happen if the reader is reading and the writer modifies it?

You should use lock, both in the reader and the writer.

If you are accessing & modifying any other resource from different threads, the same. You should lock on the same object to avoid secondary effects...

The best reference I have found about threading in general in C# is the following book:

Threading in C#, by Joseph Albahari

You will find there lots of examples like that one.

You can read it online, and I suggest you to do it, because it takes many topics related to multithreading, like Monitor.Enter and others

Edit:
If you are storing data in a local variable only because you will access to it by the main thread later, I don't think that is the best option at all. You can modify your controls in the GUI without problem using another threads, and it isn't difficult at all:

Instead of doing:

public void UpdateTextBox(string text) {
    textBox1.Text = text;
}

you can do:

public void UpdateTextBox(string text) {
    MethodInvoker m = () => { textBox1.Text = text; };

    //The following lines can be encapsulated in a method, in case you need to use it again in other methods...
    if (InvokeRequired) {
        BeginInvoke(m);
    }
    else {
        m.Invoke();
    }
}
Oscar Mederos
  • 29,016
  • 22
  • 84
  • 124
  • MSDN reads "The lock keyword marks a statement block as a critical section by obtaining the mutual-exclusion lock for a given object, executing a statement, and then releasing the lock.". Doesn't that mean that the lock will only prevent the two threads to execute the writing block at the same time? – Clément Apr 28 '11 at 08:09
  • Not only the writing, but also the reading. I'm not really following you... you need to do `lock` both in the reader and the writer because: In the reader, to tell the writer not to write while he is reading, and in the writer, to tell everyone else (the reader in this case) not to read/write while he is writing... – Oscar Mederos Apr 28 '11 at 08:15
  • What I meant was that the documentation was pretty unclear. It says that the lock will block other threads from executing the locked block, but I didn't find any mention of the fact that if you use the same object to lock two different code sections, they won't run at the same time. – Clément Apr 28 '11 at 08:24
  • The `BeginInvoke` approach really doesn't work on Mono :) See http://stackoverflow.com/questions/5804076/a-threading-problem-where-mono-hangs-and-ms-net-doesnt/ for more info. – Clément Apr 28 '11 at 08:25
1

Just to clarify, as i saw your comment, you must use a lock statement around the write operation AND a another lock statements around the reader satements. i.e.

//lock sync object in your form 
private object sync = new object();

in the writing method and in the reading method.

lock (sync)
{
    //access the array here
}
Menahem
  • 3,974
  • 1
  • 28
  • 43