Страницы

Поиск по вопросам

суббота, 13 октября 2018 г.

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

Как правильно уменьшить размер стека для .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 — в этом ответе

Комментариев нет:

Отправить комментарий