在特殊线程上运行进程并等待结果

时间:2019-02-12 17:14:56

标签: c# multithreading task-parallel-library stack-overflow

我有这个简单的代码...

var map = new ReferencedEntityMapAce(uow); ...效果很好

但是现在我需要在不同的线程上运行它(由于其递归,栈空间很大),并等待其结果再继续。

最简单的方法是什么? (我看不到给Task分配特定线程或告诉它创建具有大堆栈的线程的方法)

背景(如果需要): 上面我使用了几个月的代码突然开始引发堆栈溢出异常。我相信我已经达到了极限,因为它现在正在处理具有关系的近14万个实体,以决定应保存它们的顺序以初始化新数据库。 我无法更改递归部分-在我使用的外部第三方库中,没有计划对其进行更新。

我已经破解了测试代码,以证明当在大堆栈线程上进行处理时,它确实可以工作。

1 个答案:

答案 0 :(得分:1)

您可以将Thread类与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();
    }
}