运行 在特殊线程上处理并等待结果
Run Process on special thread and await result
我有这个简单的代码...
var map = new ReferencedEntityMapAce(uow);
...效果很好
但现在我需要在另一个线程上运行它(由于它的递归,它的堆栈大小很大)并在继续之前等待它的结果。
最简单的方法是什么?
(我看不到给 Task 一个特定线程或告诉它创建一个具有大堆栈的线程的方法)
背景(如果需要):
上面我使用了几个月的代码突然开始抛出堆栈溢出异常。我相信我刚刚达到了一个极限,因为它现在正在处理近 140k 个具有关系的实体来决定它们应该以什么顺序保存以初始化一个新数据库。
我无法更改递归部分 - 它位于我使用的外部第三方库中,没有更新它的计划。
我破解了测试代码,以证明它在处理大型堆栈线程时确实有效。
您可以将 Thread
class 与 maxStackSize
构造函数一起使用,但如果您想保持 Task
语义,则必须实现自定义 TaskScheduler
就像如下:
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();
}
}
更新:
作为自定义 TaskScheduler
的可能替代方案,可以使用 TaskCompletionSource
:
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();
}
}
我有这个简单的代码...
var map = new ReferencedEntityMapAce(uow);
...效果很好
但现在我需要在另一个线程上运行它(由于它的递归,它的堆栈大小很大)并在继续之前等待它的结果。
最简单的方法是什么? (我看不到给 Task 一个特定线程或告诉它创建一个具有大堆栈的线程的方法)
背景(如果需要): 上面我使用了几个月的代码突然开始抛出堆栈溢出异常。我相信我刚刚达到了一个极限,因为它现在正在处理近 140k 个具有关系的实体来决定它们应该以什么顺序保存以初始化一个新数据库。 我无法更改递归部分 - 它位于我使用的外部第三方库中,没有更新它的计划。
我破解了测试代码,以证明它在处理大型堆栈线程时确实有效。
您可以将 Thread
class 与 maxStackSize
构造函数一起使用,但如果您想保持 Task
语义,则必须实现自定义 TaskScheduler
就像如下:
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();
}
}
更新:
作为自定义 TaskScheduler
的可能替代方案,可以使用 TaskCompletionSource
:
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();
}
}