Можно ли положить ссылку на стек в поле и хранить в куче

Как можно ли положить референс на стек в поле и хранить в куче?


Ответы (1 шт):

Автор решения: aepot

Можно, но только осторожно.

Ссылка на область в памяти - это 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

Фактически данный код показывает, как из одного потока что-то записать в стек другому потоку. Совершенно бессмысленная операция, есть более простые и заурядные способы передачи данных, но вы спросили, возможно ли сделать ссылку на стек, я показал как.

Но вряд-ли это имеет какую-либо практическую ценность, разве только что для какой-то очень сильно специфичной задачи для многопоточки, которая требует очень высокой производительности и очень хочется избежать частых аллокаций памяти для передачи данных между потоками, и даже в таком случае есть другие, более удобные варианты решения.

Скажем так: стек - это очень быстрое хранилище небольшого объема данных для метода в одном каком-то конкретном потоке. Использовать его в многопоточке как общую память - опасно, можно очень легко накосячить с потокобезопасностью, будьте предельно внимательны.

→ Ссылка