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.)
Related
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).
Old title: Is an array of lists thread safe in C#?
I try to use a pool of lists for parallel processing. I create lists in an array and then use a ConcurrentBag of integers to allocate them to threads. Since the entire code is quite long I made an excerpt. In the below example each thread would need 5 lists of object x.
int maxThreads = 64;
ConcurrentBag<int> pool = new ConcurrentBag<int>();
// Populate ConcurrentBag with values 0 through 63
for (int i = 0; i <= maxThreads - 1; i++)
{
pool.Add(i);
}
List<x>[,] ABCList = new List<x>[maxThreads, 5];
Parallel.ForEach(zList,
new ParallelOptions { MaxDegreeOfParallelism = maxThreads },
p =>
{
while (!pool.TryTake(out slot));
// Do something here with ABCList[slot, 1....5]
// Example: ABCList[slot, 1].[1].Field1 = 2;
// Example: ABCList[slot, 1].[1].Field2 = "abc";
pool.Add(slot);
});
So this runs without an error or any warnings. However inside parallel processing it sometimes fails to update values in ABCList[slot, 1....5]. By sometimes I mean 2-5 cases out of 1000. When I check in debug mode I can't reproduce the symptom. When I press F11 the list values are updated.
Please consider that I'm a hobby programmer and I may do something obviously wrong here. Any ideas how to do this correctly?
EDIT
Based on the suggestion of Peter I rewrote like this:
int maxThreads = 64;
ConcurrentBag<int> pool = new ConcurrentBag<int>();
ConcurrentDictionary <int, List<x>[]> ABCList = new ConcurrentDictionary<int, List<x>[]>();
// Initialize ConcurrentDictionary
for (int i = 0; i < maxThreads; i++)
{
pool.Add(i);
ABCList[i] = new List<x>[5];
for (int w = 0; w <= 4; w++)
{
ABCList[i][w] = templateABCList[w].ConvertAll(p => p);
}
}
Parallel.ForEach(zList,
new ParallelOptions { MaxDegreeOfParallelism = maxThreads },
p =>
{
while (!pool.TryTake(out slot));
// Do something here with ABCList[slot][1...5]
// Example: ABCList[slot][1][1].Field1 = 2;
// Example: ABCList[slot][1][1].Field2 = "abc";
pool.Add(slot);
});
The problem still exists. The list fails to update in some cases. What do I do wrong?
You can Use System.Collections.Concurrent Name Space for Thread Safe Collections,
and you can Use ConcurrentBag Class to Create Object(List) Pool
if you want Any list in pool is Thread safe , you can Use
var threadSafeArrayList = ArrayList.Synchronized(new ArrayList());
For Create each List Object
This question already has an answer here:
Thread alters passed Int, if start() is called separately
(1 answer)
Closed 7 years ago.
I have no idea what is going on in this. I'm trying to test thread safety of a class by spawning 100 threads to access it constantly, but it seems my anonymous method parameters are changing themselves to values they should never be and I'm confused as to why. As I have no idea what's going on, I'll just post all the functions involved in testing. Somehow I'm ending up with "Thread 98" getting the parameter "num = 100"... That shouldn't be possible and I have no idea what kind of hokey pokey is going on that is changing the integer. (in method "ThreadWriting(int num)" you'll see the point where I check for "num" to equal 100, where I put a break point to catch the offending thread in the act, and it breaks every time. Otherwise it throws an "IndexOutofRangeException" on the array "counts". I'm just trying to see if my threads are generally getting equal access to the class they're all trying to use at once.
public delegate void TempDel();
public TempDel InvokeTest;
public void TRTest3(Form1 sender)
{
InvokeTest = new TempDel(UpdateInvoke);
Thread t = new Thread(() => ConsoleUpdateTest(sender));
t.IsBackground = true;
t.Start();
POConsole.Instance.MaxLines = 20;
for(int i = 0; i < 100; i++)
{
Thread t2 = new Thread(() => ThreadWriting(i));
t2.IsBackground = true;
t2.Name = String.Format("Thread {0}", i);
t2.Start();
}
}
public ulong[] counts = new ulong[100];
public void ThreadWriting(int num)
{
if(num == 100)
{
bool stop = true;
}
while (true)
{
POConsole.Instance.WriteLine("Hello from Thread " + num);
counts[num]++;
}
}
public void ConsoleUpdateTest(Form1 sender)
{
while(true)
{
sender.Invoke(InvokeTest);
Thread.Sleep(5);
}
}
public void UpdateInvoke()
{
QuickTestBox.Text = POConsole.Instance.FullFeed;
}
All my threads are named, as you can see, and none of them receives the name "Thread 100" so I have no idea how one of the other threads could get passed a parameter of 100 or the parameter could be corrupted in some way.
Apparently my thread-safety checking isn't thread safe in some way?
This is a simple closure issue, you should not be using the for loop counter as a threading parameter issue, issue happens out here, for loop and thread execution do not run at same speed, so value of i can change for multiple threads:
for(int i = 0; i < 100; i++)
{
Thread t2 = new Thread(() => ThreadWriting(i));
t2.IsBackground = true;
t2.Name = String.Format("Thread {0}", i);
t2.Start();
}
Use following modification, create a local variable from loop counter
for(int i = 0; i < 100; i++)
{
int j = i;
Thread t2 = new Thread(() => ThreadWriting(j));
t2.IsBackground = true;
t2.Name = String.Format("Thread {0}", j);
t2.Start();
}
Below is a simple example:
int Parallel_Count = int.Parse(nudParallelCount.Text);
for (int i = 1; i <= Parallel_Count; i++)
{
Thread string.Format("Thread_{0}", i) = new Thread(new ThreadStart(string.Format("Thread_{0}_Inside", i) ));
string.Format("Thread_{0}", i).Start();
}
As you see I did not use regular names for thread names and their entries and my codes have error because of them.
I want to add a counter (as a string) to thread names and thread entries names.
EDIT
One of my entries is like this :
public void Thread_1_Inside()
{
bloblobloblo -> i've created this expression by myself :)
}
You are looking for a container like an array or a list. Please make sure you understand those structures before you go into threading because they are pretty basic constructs and threading is tough stuff.
int Parallel_Count = int.Parse(nudParallelCount.Text);
Thread[] threads = new Thread[Parallel_Count];
for (int i = 0; i < Parallel_Count; i++)
{
threads[i] = new Thread(/*fill thread start here*/);
threads[i].Start();
}
A similar question was asked here, but the answers generally all seem to relate to the lambda notation. I get a similar result without the lambda so I thought I'd ask for some clarification:
Say I have something like this:
for (int i = 0; i < 5; i++)
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + i);
}))).Start();
One would expect the following output:
Thread 0
Thread 1
Thread 2
Thread 3
Thread 4
Now I realise that the threads aren't started in any particular order, so let's just assume that the above lines can come out in any order.
But that is not what happens.
What instead happens:
Thread 3
Thread 4
Thread 4
Thread 4
Thread 4
or something similar, which leads me to believe that rather than passing the value if i, it is passing the reference. (Which is weird, since an int is a value type).
Doing something like this:
for (int i = 0; i < 5; i++)
(new Thread(new ThreadStart(delegate()
{
int j = i;
Console.WriteLine("Thread " + j);
}))).Start();
does not help either, even though we have made a copy of i. I am assuming the reason is that it hasn't made a copy of i in time.
Doing something like this:
for (int i = 0; i < 5; i++)
{
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + i);
}))).Start();
Thread.Sleep(50);
}
seems to fix the problem, however it is extremely undesirable as we're wasting 50ms on each iteration, not to mention the fact that if the computer is heavily loaded then maybe 50ms may not be enough.
Here is a sample with my current, specific problem:
Thread t = new Thread(new ThreadStart(delgate()
{
threadLogic(param1, param2, param3, param4);
}));
t.Start();
param1 = param2 = param3 = param4 = null;
with:
void threadLogic(object param1, object param2, object param3, object param4)
{
// Do some stuff here...
}
I want threadLogic() to run in its own thread, however the above code gives a null reference exception. I assume this is because the values are set to null before the thread has had a chance to start.
Again, putting a Thread.Sleep(100) works, but it is an awful solution from every aspect.
What do you guys recommend for this particular type of race condition?
You need to introduce a temporary:
for (int i = 0; i < 5; i++)
{
int temp = i; // Add this
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + temp);
}))).Start();
}
The problem is in how delegates close around the outer variable (i in your code, temp in mine). The scope is wrong (outside the for loop), so by the time the thread starts, i has already been incremented most if not all of the way.
For your second example, you need to do the same thing. Just make temporaries:
var temp1 = param1;
var temp2 = param2;
var temp3 = param3;
var temp4 = param4;
Thread t = new Thread(new ThreadStart(delgate()
{
threadLogic(temp1, temp2, temp3, temp4);
}));
t.Start();
// This is now safe, since the closure above is over "temp*"
param1 = param2 = param3 = param4 = null;
Your issue is the same; it's not the lambda syntax itself, it's the fact that you're closing over a local variable in an anonymous method (the delegate syntax you're using was the first iteration of anonymous methods, which made its debut in .NET 2.0).
If you want to do this, you'll have you use a workaround:
for (int i = 0; i < 5; i++)
{
int j = i;
(new Thread(new ThreadStart(delegate()
{
Console.WriteLine("Thread " + j);
}))).Start();
}
Note that this is similar to what you tried (copying), but it needs to be outside of the closure and inside the loop. Copying it within the anonymous function (like in your example) doesn't help.