Как правильно уменьшить размер стека?

1,00
р.
Как правильно уменьшить размер стека для .NET приложения? Возможно есть какие либо директивы или опции в Visual Studio.

Ответ
Вы можете, например, воспользоваться конструктором Thread с указанием максимального размера стека.

Если вы планируете запускать Task на этом потоке, имеет смысл реализовать TaskScheduler, который перекинет Task в этот поток.
Или можно воспользоваться готовым scheduler'ом, например, WPF.

Пример кода:
При помощи этого метода можно «перебросить» async-метод в поток, на котором бежит данный WPF-диспетчер:
static class AsyncHelper { public static DispatcherRedirector RedirectTo(Dispatcher d) { return new DispatcherRedirector(d) } }
// http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx public struct DispatcherRedirector : INotifyCompletion { public DispatcherRedirector(Dispatcher dispatcher) { this.dispatcher = dispatcher }
#region awaiter public DispatcherRedirector GetAwaiter() { // combined awaiter and awaitable return this } #endregion
#region awaitable public bool IsCompleted { get { // true means execute continuation inline return dispatcher.CheckAccess() } }
public void OnCompleted(Action continuation) { dispatcher.BeginInvoke(continuation) }
public void GetResult() { } #endregion
Dispatcher dispatcher }
Теперь вам нужен поток, в котором бежит диспетчер.
public class DispatcherThread : IDisposable { public Dispatcher Dispatcher { get private set } public TaskScheduler TaskScheduler { get private set }
Thread thread
public DispatcherThread(int maxStackSize) { using (var barrier = new AutoResetEvent(false)) { thread = new Thread(() => { Dispatcher = Dispatcher.CurrentDispatcher barrier.Set() Dispatcher.Run() }, maxStackSize)
thread.SetApartmentState(ApartmentState.STA) thread.Start() barrier.WaitOne() }
TaskScheduler = Get(() => TaskScheduler.FromCurrentSynchronizationContext()) }
// --------------------------------------------- // остальные функции вам не нужны для вашей задачи, но могут пригодиться впоследствии public void Execute(Action a) { if (Dispatcher.CheckAccess()) a() else Dispatcher.Invoke(a) }
public void FireAndForget(Action a) { Dispatcher.BeginInvoke(a) }
public T Get(Func getter) { if (Dispatcher.CheckAccess()) return getter() else { T t = default(T) Dispatcher.Invoke((Action)(() => { t = getter() })) return t } }
public Task GetAsync(Func getter) { return Dispatcher.InvokeAsync(getter).Task }
public Task StartNewTask(Action action) { return Task.Factory.StartNew( action: action, cancellationToken: CancellationToken.None, creationOptions: TaskCreationOptions.None, scheduler: TaskScheduler) }
public Task StartNewTask(Func function) { return Task.Factory.StartNew( function: function, cancellationToken: CancellationToken.None, creationOptions: TaskCreationOptions.None, scheduler: TaskScheduler) }
public void Dispose() { Dispatcher.InvokeShutdown() if (thread != Thread.CurrentThread) thread.Join() } }
Этим можно пользоваться, например, так:
using (var t = new DispatcherThread(maxStackSize)) { await AsyncHelper.RedirectTo(t.Dispatcher) // остаток метода }

Обновление: совсем забыл, надо же по идее сбежать из умирающего потока! Например, в thread pool.
static class AsyncHelper { public static ThreadPoolRedirector RedirectToThreadPool() { return new ThreadPoolRedirector() } }
public struct ThreadPoolRedirector : INotifyCompletion { #region awaiter public ThreadPoolRedirector GetAwaiter() { // combined awaiter and awaitable return this } #endregion
#region awaitable public bool IsCompleted { get { // true means execute continuation inline return Thread.CurrentThread.IsThreadPoolThread } }
public void OnCompleted(Action continuation) { ThreadPool.QueueUserWorkItem(o => continuation()) }
public void GetResult() { } #endregion }
и использовать как
using (var t = new DispatcherThread(maxStackSize)) { await AsyncHelper.RedirectTo(t.Dispatcher) // остаток метода await AsyncHelper.RedirectToThreadPool() }

Хотя может быть это и не нужно, InvokeShutdown не убивает поток немедленно. Но тем не менее.

Более современная версия DispatcherThread — в этом ответе.