Как правильно уменьшить размер стека для .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
public Task
public Task StartNewTask(Action action)
{
return Task.Factory.StartNew(
action: action,
cancellationToken: CancellationToken.None,
creationOptions: TaskCreationOptions.None,
scheduler: TaskScheduler);
}
public Task
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 — в этом ответе
Комментариев нет:
Отправить комментарий