When using anonymous delegate
s in C# the CLR will generate a copy of the local (e.g. variables in the current scope) on the heap for used variables. Such a local will be put onto the heap for every declared variable of the current scope.
You can see this behavior on this example:
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
ThreadPool.QueueUserWorkItem(delegate { execute(i); });
Thread.Sleep(1000);
Console.WriteLine();
for (int i = 0; i < 5; i++)
{
int j = i;
ThreadPool.QueueUserWorkItem(delegate { execute(j); });
}
Thread.Sleep(1000);
}
static void execute(int number)
{
Console.WriteLine(" * NUM=" + number.ToString());
}
}
The output of this program is (the order may vary on the last 5 entries while on the first one it's also possible to get lower numbers than 5.):
* NUM=5
* NUM=5
* NUM=5
* NUM=5
* NUM=5
* NUM=0
* NUM=1
* NUM=2
* NUM=3
* NUM=4
C# should always generate a new copy of the local when called in a method. This is working as intended in this example:
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
call(i);
Thread.Sleep(1000);
}
static void call(int number)
{
ThreadPool.QueueUserWorkItem(delegate { execute(number); });
}
static void execute(int number)
{
Console.WriteLine(" * NUM=" + number.ToString());
}
}
Output:
* NUM=0
* NUM=1
* NUM=2
* NUM=3
* NUM=4
This is the case in question: However, it's not working when assigning the variable to a stackalloc
reserved area:
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
call(i);
Thread.Sleep(1000);
}
static unsafe void call(int number)
{
int* ints = stackalloc int[64];
ints[32] = number;
ThreadPool.QueueUserWorkItem(delegate { execute(ints[32]); });
}
static void execute(int number)
{
Console.WriteLine(" * NUM=" + number.ToString());
}
}
Output:
* NUM=4
* NUM=4
* NUM=4
* NUM=4
* NUM=4
When using a regular local variable - just replace the call
method from the example above:
static void call(int number)
{
int j = number;
ThreadPool.QueueUserWorkItem(delegate { execute(j); });
}
Output:
* NUM=0
* NUM=1
* NUM=2
* NUM=3
* NUM=4
This situation makes me not trust anonymous delegate
s in C# - because I don't understand when exactly C# won't fuck up my calls to anonymous delegate
s.
My Questions: Why does C# not keep track of the stackalloc
space regarding anonymous delegate
s? I'm aware of C# not keeping track. I want to know why it's not keeping track, if it does with a regular variable.
I used .NET Core 2.1 with C# 7.3 including /unsafe
switch for those examples.