How to properly reduce the size of the stack for .NET applications? Perhaps there are any directives or options in Visual Studio.

  • one
    So, already warmer. And why do you need 2000 threads? Maybe it is better to switch to async / await? - VladD
  • one
    (1) To increase the number of threads, the idea is not to increase, but to reduce the stack. (2) It is still not clear why not async / await. You can write a TCP server in async / await. Allocating to each request by stream is too expensive. - VladD
  • 2
    But still, it seems to me that you are going the wrong way. Allocating a separate thread to each request is very costly, and you thereby impose a strict limit on the number of simultaneous requests. You can be easily zadosit simply by opening 20,000 connections and slowly responding to requests. Why is it still on async / await? - VladD
  • one
    Try ConnectAsync yourself, you will be surprised :) Flows are not needed. - VladD
  • 3
    It's hard for me to talk about your particular application, but for a good network code, synchronous processing in separate streams is very atypical. I would not advise to go this way, but instead "asynchronize" your application. But you, as the architect of the project, in any case know better. - VladD

1 answer 1

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 the Task on 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: LongRunning simply runs the Task on the thread pool. - VladD
  • one
    @ z668: That's good :) No thanks, information should be free! - VladD