When there are multiple threads writing to the same variable, one simple way to keep consistency is to lock the variable so only one thread can write to it at a time.
The following is an example. Both child threads update the text field with its own thread id and print the text out. The loop is only making the thread to run longer.
An intuitive idea is to lock the "text" field, but actually it's a bad idea and it wont work as expected. The part goes wrong is where "text = ...", i.e. writing to the text field.
The lock is initially applied on a string object, but within the lock the string object is changed to a different one. So when a second thread starts running, the string object may have been changed already and there is no lock on the new string object. So the second thread can go straight to edit the text field as if the lock is not there (actually it's still on the original object but the text is a new object now).
The best practice is to create a dedicated readonly object (not string or basic types) only for locking purpose.
In this example, the text_lock is applied with a lock, and it's never edited again within the code. When the second thread runs, it realizes the lock on text_lock is not released yet so it keeps waiting until the first thread finishes. As a consequence, we will see all the loop items in thread 1 finish before thread 2 can run.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace TestMultiThread
{
public class ThreadExample
{
static readonly object text_lock = new object();
static String text = "";
public static void dowork()
{
//lock (text)
lock (text_lock)
{
text = "[" + Thread.CurrentThread.ManagedThreadId + "] is here.";
for (int i = 0; i < 20; i++)
{
Console.WriteLine(text +"| Text Id: "+text.GetHashCode()+" | "+ " Child thread: [" + Thread.CurrentThread.ManagedThreadId + "] loop: {0}" , i);
}
}
}
public static void Main()
{
Thread t1 = new Thread(dowork);
Thread t2 = new Thread(dowork);
t1.Start();
t2.Start();
//block the main thread until both child threads finish
t2.Join();
t1.Join();
Console.ReadLine();
}
}
}
Below is the result with text_lock. The second thread (id 10) waits until the first thread (id 9) finishes.
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 0
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 1
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 2
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 3
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 4
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 5
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 6
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 7
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 8
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 9
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 10
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 11
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 12
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 13
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 14
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 15
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 16
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 17
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 18
[9] is here.| Text Id: -300020054 | Child thread: [9] loop: 19
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 0
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 1
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 2
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 3
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 4
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 5
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 6
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 7
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 8
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 9
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 10
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 11
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 12
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 13
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 14
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 15
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 16
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 17
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 18
[10] is here.| Text Id: -1582670304 | Child thread: [10] loop: 19
If we lock the 'text' instead, the result will be wrong. In below, thread id 11 edits text to (id 1670973765). As 1670973765 is not the original text, thread id 12 doesn't wait and change the text straight away (id 340418990). Even worse thing is thread 12 changes the text when thread 11 is in the middle of running loop 1. So anything is just messed up.
[11] is here.| Text Id: 1670973765 | Child thread: [11] loop: 0
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 1
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 2
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 3
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 4
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 5
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 6
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 7
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 0
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 1
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 2
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 3
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 4
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 5
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 6
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 7
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 8
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 9
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 10
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 11
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 12
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 13
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 14
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 15
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 16
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 17
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 18
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 8
[12] is here.| Text Id: 340418990 | Child thread: [12] loop: 19
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 9
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 10
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 11
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 12
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 13
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 14
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 15
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 16
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 17
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 18
[12] is here.| Text Id: 340418990 | Child thread: [11] loop: 19