How to properly reduce the size of the stack for .NET applications? Perhaps there are any directives or options in Visual Studio.
1 answer
You can, for example, use the Thread constructor with the maximum stack size.
If you plan to run Task on this thread, it makes sense to implement TaskScheduler, which will throw Task into this thread.
Or you can use a ready-made scheduler, for example, WPF.
Code example:
Using this method, you can “transfer” the async method to the stream on which this WPF dispatcher runs:
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; } Now you need a thread in which the dispatcher is running.
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<T>(Func<T> getter) { if (Dispatcher.CheckAccess()) return getter(); else { T t = default(T); Dispatcher.Invoke((Action)(() => { t = getter(); })); return t; } } public Task<T> GetAsync<T>(Func<T> 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<T> StartNewTask<T>(Func<T> 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(); } } This can be used, for example, as follows:
using (var t = new DispatcherThread(maxStackSize)) { await AsyncHelper.RedirectTo(t.Dispatcher); // остаток метода } Update: I completely forgot, one must in theory run away from the dying flow! For example, in 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 } and use as
using (var t = new DispatcherThread(maxStackSize)) { await AsyncHelper.RedirectTo(t.Dispatcher); // остаток метода await AsyncHelper.RedirectToThreadPool(); } Although it may not be necessary, InvokeShutdown does not kill the stream immediately . But nonetheless.
A more modern version of DispatcherThread is in this answer .
- And what about Task? I do not use Thread. - Alexis
- one@ z668: Okay, then you need your own
TaskScheduler, which will execute theTaskon the thread or threads you specify. - VladD - Thank you, I think it is worth adding this in response. Went to pick the LongRunning source. - Alexis
- one@ z668:
LongRunningsimply runs theTaskon the thread pool. - VladD - one@ z668: That's good :) No thanks, information should be free! - VladD
ConnectAsyncyourself, you will be surprised :) Flows are not needed. - VladD