I had this simple code...
var map = new ReferencedEntityMapAce(uow);
... which worked fine
but now I need to run it on a different thread (with a large stack size due to its recursion) and await its result before continuing.
What is the simplest way of doing this? (I can't see a way of giving Task a specific thread or telling it to create one with a large stack)
Background (if needed): The code above that I had been using for months suddenly starting throwing a stack overflow exception. I believe I have just hit a limit since it is now processing nearly 140k entities with relationships to decide what order they should be saved to initialize a new database. I cannot alter the recursion part - that is in an external third-party library I use with no plans to update it.
I have hacked up test code to prove that it does indeed work when procesed on a large stack thread.
You can use Thread
class with the maxStackSize
constructor but if you want to keep Task
semantic you have to implement custom TaskScheduler
like as follows:
public class BigStackTaskScheduler : TaskScheduler
{
private int _stackSize;
public BigStackTaskScheduler(int stackSize)
{
_stackSize = stackSize;
}
// we don't need to keep a tasks queue here
protected override IEnumerable<Task> GetScheduledTasks()
{
return new Task [] { };
}
protected override void QueueTask(Task task)
{
var thread = new Thread(ThreadWork, _stackSize);
thread.Start(task);
}
// we aren't going to inline the execution
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
QueueTask(task);
return false;
}
private void ThreadWork(object obj)
{
if (obj is Task task)
TryExecuteTask(task);
}
}
class Program
{
async static Task Test()
{
var taskFactory = new TaskFactory(
CancellationToken.None, TaskCreationOptions.DenyChildAttach,
TaskContinuationOptions.None, new BigStackTaskScheduler(0xffff * 2));
await taskFactory.StartNew(() => { Console.WriteLine("Task"); });
}
static void Main(string[] args)
{
Test().Wait();
}
}
Update:
As possible alternative of custom TaskScheduler
the TaskCompletionSource
can be used:
class Program
{
static Task<TOut> ThreadWithCustomStack<TIn, TOut>(Func<TIn, TOut> action, TIn arg, int stackSize)
{
var tcs = new TaskCompletionSource<TOut>();
var thread = new Thread(new ThreadStart(() =>
{
try
{
tcs.SetResult(action(arg));
}
catch (Exception e)
{
tcs.SetException(e);
}
}), stackSize);
thread.Start();
thread.Join();
return tcs.Task;
}
async static Task Test()
{
var result = await ThreadWithCustomStack(
arg => { Console.WriteLine("Task"); return arg.ToString(); },
2,
0xffff * 2);
}
static void Main(string[] args)
{
Test().Wait();
}
}