Можно ли положить ссылку на стек в поле и хранить в куче
Как можно ли положить референс на стек в поле и хранить в куче?
Ответы (1 шт):
Можно, но только осторожно.
Ссылка на область в памяти - это IntPtr/UIntPtr, число. А что можно делать с числами, вы знаете. Правда чтобы достать это число, придется использовать небезопасный код, потому что разработчики .NET неплохо позаботились о том, чтобы вы не попортили себе данные в стеке.
Данный трюк выполнен профессионалом, не пытайтесь повторить это дома.
unsafe class MyClass
{
private IntPtr ptr;
public byte[] Method1(ManualResetEventSlim evt, ManualResetEventSlim init)
{
Span<byte> span = stackalloc byte[4]; // аллокация 4 байт в стеке
fixed (void* p = span) // получаю указатель
{
ptr = new IntPtr(p); // сохраняю его в поле
init.Set(); // сообщаю вызвающему коду, что я готов принять данные
evt.Wait(); // жду, когда вызывающий код закончит запись
}
return span.ToArray(); // копирую данные в обычный массив и возвращаю
}
public void Method2()
{
Span<byte> span = new Span<byte>(ptr.ToPointer(), 4); // делаю спан из указателя
for (byte i = 0; i < span.Length; i++)
{
span[i] = i; // записываю в него данные
}
}
}
Вызывающий код
static void Main(string[] args)
{
MyClass myClass = new MyClass();
ManualResetEventSlim evt = new();
ManualResetEventSlim init = new();
Task<byte[]> task = Task.Run(() => myClass.Method1(evt, init));
init.Wait();
myClass.Method2();
evt.Set();
byte[] result = task.Result;
Console.WriteLine(string.Join(",", result));
}
Вывод в консоль
0,1,2,3
Фактически данный код показывает, как из одного потока что-то записать в стек другому потоку. Совершенно бессмысленная операция, есть более простые и заурядные способы передачи данных, но вы спросили, возможно ли сделать ссылку на стек, я показал как.
Но вряд-ли это имеет какую-либо практическую ценность, разве только что для какой-то очень сильно специфичной задачи для многопоточки, которая требует очень высокой производительности и очень хочется избежать частых аллокаций памяти для передачи данных между потоками, и даже в таком случае есть другие, более удобные варианты решения.
Скажем так: стек - это очень быстрое хранилище небольшого объема данных для метода в одном каком-то конкретном потоке. Использовать его в многопоточке как общую память - опасно, можно очень легко накосячить с потокобезопасностью, будьте предельно внимательны.