Strange Multi-Threading Index out of bound Issue - c#

No matter what I use: Threading Class or TPL task based pattern. There is always an Index out of bound on the data.
From further research, I found the value of counter i can be 4, which should not be even possible.
What I have Missed? I'm expecting your expert opinions!
Tested with Visual Studio 15.8(2017) 16.1(2019), project targeting .NET framework 4.72.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
// a multi-threading search demo, omit much code for simple and clear
// generate 0-99, total 100 elements with ascending order
List<int> testData = new List<int>();
for (int i = 0; i < 100; i++)
{
testData.Add(i);
}
List<int> searchFor = new List<int>() {
67, 0, 99,
23, 24, 25,
-1, 106
};
const int threadsCount = 4;
// Test switch
bool useThreadInsteadOfTaskTPL = true;
if (useThreadInsteadOfTaskTPL)
{
// search every piece of data
for (int j = 0; j < searchFor.Count; j++)
{
Thread[] threads = new Thread[threadsCount];
Console.WriteLine("Search for: {0}", searchFor[j]);
// trying to divide the data into 4 parts, and search in parallel
for (int i = 0; i < threadsCount; i++)
{
Thread thread = new Thread(() => {
// Capture the counters to make sure no lambda pitfall
int counterI = i;
int counterJ = j;
Console.WriteLine("i value: {0}", counterI);
Console.WriteLine("j value: {0}", counterJ);
// your code
});
threads[i] = thread;
threads[i].Start();
}
for (int i = 0; i < threads.Length; i++)
{
threads[i].Join();
}
Console.WriteLine();
}
}
else
{
for (int j = 0; j < searchFor.Count; j++)
{
Task[] tasks = new Task[threadsCount];
Console.WriteLine("Search for: {0}", searchFor[j]);
// trying to divide the data into 4 parts, and search in parallel
for (int i = 0; i < threadsCount; i++)
{
Task task = Task.Factory.StartNew(() => {
// Capture the counters to make sure no lambda pitfall
int counterI = i;
int counterJ = j;
Console.WriteLine("i value: {0}", counterI);
Console.WriteLine("j value: {0}", counterJ);
// your code
}, new CancellationTokenSource().Token,
TaskCreationOptions.None, TaskScheduler.Default);
tasks[i] = task;
}
Task.WaitAll(tasks);
Console.WriteLine();
}
}
Console.ReadKey();
}
}
}
The expected value of i should go through 0...3,
but the actual value of i may equals to 4 or keep unchanged between iterates.

You should reassign i and j on loop start (not inside lambda):
for (int i = 0; i < threadsCount; i++)
{
// Capture the counters to make sure no lambda pitfall
int counterI = i;
int counterJ = j;
Thread thread = new Thread(() =>
{
Console.WriteLine("i value: {0}", counterI);
Console.WriteLine("j value: {0}", counterJ);
// your code
}
}
Your thread is scheduled for execution (it is not started immediately after Start() is called) and when it starts running the value of i (and j) can be already changed. (You can take a look at compiler generated code for this case and for yours).
And same for tasks - they are scheduled, not started immediately.
More details:
See this example (Action delegate is used instead of Thread) and generated code.
You can see difference (generated code creates instance of class
which stores value to print and a method which actually prints):
reassign inside delegate - for every iteration the same instance is used and value is incremented after calling the delegate. With Action it works as expected,
because it executes immediately (calling method from generated class
to print value), then value of generated class is incremented and new
iteration is started.
reassign outside delegate - instance of generated class is created
for every iteration, so there is no increment. Every iteration has
independent instance and next iteration can't change the value for
previous one.
In case of threads the only difference is that thread is not started immediately, it is scheduled for execution and this takes some time. For first case - when method for printing value is called, the value can be already incremented (because of same instance for all iterations) and you get unexpected result.
You can check this by running application multiple times (for first case) - you will get not identical results when printing i variable - sometimes it is incremented when it is not expected (because it took some time from calling Start() and actual starting of thread execution after scheduling), sometimes values are correct (because thread was scheduled and started almost immediately after calling Start() before increment occurs).

Related

C# Thread example code working without a lock

I'm having a play with Threads to remind myself how they work, I haven't done any Threading code in ages.
So I thought I'd start with the most basic example, create n threads, get them updating a static int without a lock, so I can see it go wrong. However, it's working? The final value is 500 every time, it should be slightly random as Threads update the value at the same time
I know I'm doing something really stupid here but I can't see what
https://dotnetfiddle.net/w9TK5W
using System;
using System.Threading;
public class Program
{
public class Department
{
public static int a = 0;
public Department()
{
}
public void Inc()
{
a = a +5;
a = a -4;
}
}
public static void Main()
{
int count = 500;
Thread[] threads = new Thread[count];
Department dep = new Department();
for (int i = 0; i < count; i++)
{
Thread t = new Thread(new ThreadStart(dep.Inc));
threads[i] = t;
}
for (int i = 0; i < count; i++)
{
threads[i].Start();
}
Thread.Sleep(2000);
for (int i = 0; i < count; i++)
{
threads[i].Join();
}
Console.WriteLine(Department.a.ToString());
}
}
[Edit, more info]
So I changed the loop to look like this, it now works as expected
int b = a;
b = b+1;
int j=0;
for (int i=0; i<1E5; i++)
{
j += i;
}
a = b;
The computation in each thread is so short that probably each started thread is already ended before the next one actually starts.
This happens just by chance.
You should run in each thread a loop that performs millions of operations on a, and then you will probably detect the expected inconsistency.
The question has been edited with millions of operation which do not alter a, thus by chance again, the very few operations that actually alter a don't happen at the same time.
The question has been edited since the previous remark, but now it fails for a different reason than the expected initial one.
Reading a, waiting a bit, and altering a based on the read value is obviously not atomic.
If the loop simply performed a+=1, you could also see that this apparently trivial operation is not atomic either.

Parallel.Foreach loop gets different result than For loop?

I've
written simple for loop iterating through array and Parallel.ForEach loop doing the same thing. However, resuls I've get are different so I want to ask what the heck is going on? :D
class Program
{
static void Main(string[] args)
{
long creating = 0;
long reading = 0;
long readingParallel = 0;
for (int j = 0; j < 10; j++)
{
Stopwatch timer1 = new Stopwatch();
Random rnd = new Random();
int[] array = new int[100000000];
timer1.Start();
for (int i = 0; i < 100000000; i++)
{
array[i] = rnd.Next(5);
}
timer1.Stop();
long result = 0;
Stopwatch timer2 = new Stopwatch();
timer2.Start();
for (int i = 0; i < 100000000; i++)
{
result += array[i];
}
timer2.Stop();
Stopwatch timer3 = new Stopwatch();
long result2 = 0;
timer3.Start();
Parallel.ForEach(array, (item) =>
{
result2 += item;
});
if (result != result2)
{
Console.WriteLine(result + " - " + result2);
}
timer3.Stop();
creating += timer1.ElapsedMilliseconds;
reading += timer2.ElapsedMilliseconds;
readingParallel += timer3.ElapsedMilliseconds;
}
Console.WriteLine("Create : \t" + creating / 100);
Console.WriteLine("Read: \t\t" + reading / 100);
Console.WriteLine("ReadP: \t\t" + readingParallel / 100);
Console.ReadKey();
}
}
So in the condition I get results:
result = 200009295;
result2 = 35163054;
Is there anything wrong?
The += operator is non-atomic and actually performs multiple operations:
load value at location that result is pointing to, into memory
add array[i] to the in-memory value (I'm simplifying here)
write the result back to result
Since a lot of these add operations will be running in parallel it is not just possible, but likely that there will be races between some of these operations where one thread reads a result value and performs the addition, but before it has the chance to write it back, another thread grabs the old result value (which hasn't yet been updated) and also performs the addition. Then both threads write their respective values to result. Regardless of which one wins the race, you end up with a smaller number than expected.
This is why the Interlocked class exists.
Your code could very easily be fixed:
Parallel.ForEach(array, (item) =>
{
Interlocked.Add(ref result2, item);
});
Don't be surprised if Parallel.ForEach ends up slower than the fully synchronous version in this case though. This is due to the fact that
the amount of work inside the delegate you pass to Parallel.ForEach is very small
Interlocked methods incur a slight but non-negligible overhead, which will be quite noticeable in this particular case

Multithreading issue updating the value

I have difficulty understanding why the multithreading fails to update values before the thread completes. Does the separate thread have its own copy of the references or values?
If not, to my understanding the code below should work properly when MyMethod is called, but often it does not create instances of some MyType objects in the array before the thread.IsAlive becomes false:
class MyClass
{
static MyType[] obj = new MyType[Environment.ProcessorCount - 1];
void MyMethod()
{
Thread[] threads = new Thread[Environment.ProcessorCount - 1];
for (int i = 0; i < Environment.ProcessorCount - 1; i++)
{
threads[i] = new Thread(() => FillObjects(i));
threads[i].Priority = ThreadPriority.AboveNormal;
threads[i].Start();
}
while (threads[i].Any(c => c.IsAlive))
{
Thread.Sleep(50);
}
}
void FillObjects(int i)
{
obj[i] = new MyType();
//perform actions with obj[i] to fill it with necessary values
}
}
You need to assign the value of the loop variable to a local variable. Otherwise it's possible that the first execution of FillObjects(i) is executed after i was incremented so FillObjects(0) is never called and therefore obj[0] is never assigned.
void MyMethod()
{
Thread[] threads = new Thread[Environment.ProcessorCount - 1];
for (int i = 0; i < Environment.ProcessorCount - 1; i++)
{
int local = i;
threads[i] = new Thread(() => FillObjects(local));
threads[i].Priority = ThreadPriority.AboveNormal;
threads[i].Start();
}
while (threads.Any(c => c.IsAlive))
{
Thread.Sleep(50);
}
}
On a multi processor machine (which you must have) results written to a memory location in one thread may not be visible in another thread due to caching. Use Thread.VolatileRead and Thread.VolatileWrite read in order to read and write "through" the cache.
Cf. the chapter on threading in c# 3.0 in a Nutshell for an explanation. (Look for the question "Is it possible for the Wait method to write "False"?". That example is basically your case.)

Conflicting threads on a local variable

why is it, that in the following code, n doesn't end up being 0, it's some random number with a magnitude less than 1000000 each time, somtimes even a negative number?
static void Main(string[] args)
{
int n = 0;
var up = new Thread(() =>
{
for (int i = 0; i < 1000000; i++)
{
n++;
}
});
up.Start();
for (int i = 0; i < 1000000; i++)
{
n--;
}
up.Join();
Console.WriteLine(n);
Console.ReadLine();
}
Doesn't up.Join() force both for loops to finish before WriteLine is called?
I understand that the local variable is actually part of a class behind the scenes (think it's called a closure), however because the local variable n is actually heap allocated, would that affect n not being 0 each time?
The n++ and n-- operations are not guaranteed to be atomic. Each operation has three phases:
Read current value from memory
Modify value (increment/decrement)
Write value to memory
Since both of your threads are doing this repeatedly, and you have no control over the scheduling of the threads, you will have situations like this:
Thread1: Get n (value = 0)
Thread1: Increment (value = 1)
Thread2: Get n (value = 0)
Thread1: Write n (n == 1)
Thread2: Decrement (value = -1)
Thread1: Get n (value = 1)
Thread2: Write n (n == -1)
And so on.
This is why it is always important to lock access to shared data.
-- Code:
static void Main(string[] args)
{
int n = 0;
object lck = new object();
var up = new Thread(() =>
{
for (int i = 0; i < 1000000; i++)
{
lock (lck)
n++;
}
});
up.Start();
for (int i = 0; i < 1000000; i++)
{
lock (lck)
n--;
}
up.Join();
Console.WriteLine(n);
Console.ReadLine();
}
-- Edit: more on how lock works...
When you use the lock statement it attempts to acquire a lock on the object you supply it - the lck object in my code above. If that object is already locked, the lock statement will cause your code to wait for the lock to be released before continuing.
The C# lock statement is effectively the same as a Critical Section. Effectively it is similar to the following C++ code:
// declare and initialize the critical section (analog to 'object lck' in code above)
CRITICAL_SECTION lck;
InitializeCriticalSection(&lck);
// Lock critical section (same as 'lock (lck) { ...code... }')
EnterCriticalSection(&lck);
__try
{
// '...code...' goes here
n++;
}
__finally
{
LeaveCriticalSection(&lck);
}
The C# lock statement abstracts most of that away, meaning that it's much harder for us to enter a critical section (acquire a lock) and forget to leave it.
The important thing though is that only your locking object is affected, and only with regard to other threads trying to acquire a lock on the same object. Nothing stops you from writing code to modify the locking object itself, or from accessing any other object. YOU are responsible for making your sure your code respect the locks, and always acquires a lock when writing to a shared object.
Otherwise you're going to have a non-deterministic outcome like you've seen with this code, or what the spec-writers like to call 'undefined behavior'. Here Be Dragons (in the form of bugs you'll have endless trouble with).
Yes, up.Join() will ensure that both of the loops end before WriteLine is called.
However, what is happening is that the both of the loops are being executed simultaneously, each one in it's own thread.
The switching between the two threads is done all the time by the operation system, and each program run will show a different switching timing set.
You should also be aware that n-- and n++ are not atomic operations, and are actually being compiled to 3 sub-operations, e.g.:
Take value from memory
Increase it by one
Put value in memory
The last piece of the puzzle, is that the thread context switching can occur inside the n++ or n--, between any of the above 3 operations.
That is why the final value is non-deterministic.
If you don't want to use locks there are atomic versions of the increment and decrement opperators in the Interlocked class.
Change your code to the following and you will always get 0 for an answer.
static void Main(string[] args)
{
int n = 0;
var up = new Thread(() =>
{
for (int i = 0; i < 1000000; i++)
{
Interlocked.Increment(ref n);
}
});
up.Start();
for (int i = 0; i < 1000000; i++)
{
Interlocked.Decrement(ref n);
}
up.Join();
Console.WriteLine(n);
Console.ReadLine();
}
You need to join the threads earlier:
static void Main(string[] args)
{
int n = 0;
var up = new Thread(() =>
{
for (int i = 0; i < 1000000; i++)
{
n++;
}
});
up.Start();
up.Join();
for (int i = 0; i < 1000000; i++)
{
n--;
}
Console.WriteLine(n);
Console.ReadLine();
}

Multi-threads not terminating after for loop executes

I have n number of threads which run a method routine. With each thread running the search method, and searching for items in a given range, the items are retrieved from a web server. But I believe that they keep on running, even when the range is exhausted. I am using a for loop to determine when the thread should stop searching, but that is not working out. This is what I have:
In method Start() I compute a certain range, and that range is given to a thread, which is to search in this given range.
public void Start()
{
this.totalRangePerThread = ((this.endRange - this.startRange) / this.subWorkerThreads.Length);
for (int i = 0; i < this.subWorkerThreads.Length; ++i)
{
var copy = startRange;
this.subWorkerThreads[i] = new Thread(() => searchItem(copy, this.totalRangePerThread));
this.startRange = this.startRange + this.totalRangePerThread;
}
for (int threadIndex = 0; threadIndex < this.subWorkerThreads.Length; ++threadIndex)
this.subWorkerThreads[threadIndex].Start();
}
This is my SearchItem() method:
public void searchItem(int start, int pagesToSearchPerThread)
{
for (int count = 0; count < pagesToSearchPerThread; ++count)
{
start++;
for (int activeListCount = 0; activeListCount < this.activeListItems.Count; ++activeListCount)
{
//further method calls here to webservers..
}
}
}
I know about using some shared sentinel to determine when to stop a thread, but I fail to comprehend how to apply it here? How should I be handling this scenario, such that a thread aborts gracefully, when its task is completed...

Categories

Resources