Multi-threads not terminating after for loop executes - c#

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...

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.

Strange Multi-Threading Index out of bound Issue

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).

Simultaneous printing on the console in c#, is it possible?

I'm new to programming and currently I'm trying to learn c#. Here is my question:
I have this following piece of code:
static void Main()
{
string loading = "LOADING...";
for (int i = 0; i < 5; i++)
{
foreach (var letter in loading)
{
Console.Write("{0}", letter);
Thread.Sleep(250);
}
Console.Clear();
Console.Write("\r");
}
for (int k = 0; k <= 100; k++)
{
Console.Write("\r{0}%", k);
Thread.Sleep(150);
}
}
I am trying to find a way to execute these two for loops simultaneously. I want to keep re-writing LOADING on the console and under it to print the percentage from 1 to 100. Right now the second for loop runs after the first quits. Is it possible to make them run at the same time? I have been trying to find an answer for a few hours now, but with no luck so far.
Thanks!
As krillgar noted you can use Tasks to run each loop independently. Something like this:
string loading = "LOADING...";
var loadingTask = Task.Run(() =>
{
for (int i = 0; i < 5; i++)
{
foreach (var letter in loading)
{
Console.Write("{0}", letter);
Thread.Sleep(250);
}
Console.Clear();
Console.Write("\r");
}
});
var pocTask = Task.Run(() =>
{
for (int k = 0; k <= 100; k++)
{
Console.Write("\r{0}%", k);
Thread.Sleep(150);
}
});
Task.WaitAll(loadingTask, pocTask);
Please note however it'll not work as you would expect( first line with LOADING... and second with percentage). That would require some synchronization of displayed messages between those loops and I think that's whole another story and not in the scope of the answer to your question.
There is more to this challenge than just running the code in two threads. When multiple threads are attempting to write to the console simultaneously, you are likely to end up with race conditions, where one thread starts writing in the middle of another thread's output.
To protect against this, you need to enforce critical sections, which are regions of your code where only one thread can execute at any time. These would generally consist of:
// Critical section
lock (syncLock)
{
Console.SetCursorPosition(x, y);
Console.Write(yourText);
}
Thread.Sleep(yourDelay);
Here is the full code:
// Dummy object to serve as mutual-exclusion lock when synchronizing threads.
private static readonly object syncLock = new object();
public static void Main(string[] args)
{
// Run two anonymous functions in parallel,
// then wait for both to complete.
Parallel.Invoke(
// Anonymous function for printing "LOADING..."
() =>
{
const string loading = "LOADING...";
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < loading.Length; j++)
{
// Critical section
lock (syncLock)
{
Console.SetCursorPosition(j, 0);
Console.Write("{0}", loading[j]);
}
Thread.Sleep(250);
}
// Critical section
lock (syncLock)
{
Console.SetCursorPosition(0, 0);
Console.Write("\r ");
}
Thread.Sleep(250);
}
},
// Anonymous function for printing "x%"
() =>
{
for (int k = 0; k <= 100; k++)
{
// Critical section
lock (syncLock)
{
Console.SetCursorPosition(0, 1);
Console.Write("\r{0}%", k);
}
Thread.Sleep(150);
}
});
}
}

Dynamically assign function to threads

Want to spawn some threads and assign function to each of them later on in a loop.
Spawning the threads with
var threadList = new List<Thread>();
for (int index = 0; index < 3; index++)
{
threadList.Add(new Thread(() => { }));
}
Now in a loop want to assign function to the threads
for (int index = 0; index < names.Length; index++)
{
string tempName = names[index];
//check which thread is available
//assign a function to the first available thread
}
How do i do it?
If i am using a threadpool:
how do i get notified when all the threads are done? how do i stop the threadpool to perform any qction in queue?
Couldn't you just use ThreadPool instead?
System.Threading.ThreadPool.QueueUserWorkItem(myMethod);
This will assign your methods to an available thread which will be freed up when done.

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();
}

Categories

Resources