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.
Related
I want to create 10 threads with different names. Name should be a combination of name and id.ToString()
If I make a thread using Thread t = new Thread() then I know that I can do t.Name = name + id.ToString();, but if I there is some way to do it with the code example below I would love to know it.
string name = "Thread #";
int id = 0;
for (int i = 0; i < 10; i++)
{
new Thread(() =>
{
for (int j = 0; j <= 100; j++)
{
//Do something
}
}).Start();
}
you could do
var t = new Thread(...);
t.Name = "Thread " + i;
t.Start();
But you should probably use the thread pool instead of creating explicit threads. In this specific case where you have a loop, you should probably use a Parallel.For. In general when you want to run something on a background thread you should use Task.Run, or in some cases Dataflow. When using the task pool you should not name threads, since the threads may be reused for other things. See How can I assign a name to a task in TPL for similar concept for tasks.
Using the thread pool reduces the overhead for running things in the background, and usually also provides interfaces that are easier to use.
I just added Thread t = Thread.CurrentThread; and then I could do t.Name = $"Thread #{id}"; after.
This is what it looks like in its' entirety:
int tId = 0;
for (int i = 0; i < 10; i++)
{
new Thread(() =>
{
Thread t = Thread.CurrentThread;
tId++;
t.Name = $"Thread #{tId}";
Console.WriteLine($"{t.Name}");
}).Start();
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.
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).
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.)
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();
}