Запустите процесс на специальном потоке и дождитесь результата - PullRequest
0 голосов
/ 12 февраля 2019

У меня был этот простой код ...

var map = new ReferencedEntityMapAce(uow); ... который работал нормально

, но теперь мне нужно запустить его в другом потоке (с большим размером стека из-задо его рекурсии) и дождитесь его результата, прежде чем продолжить.

Какой самый простой способ сделать это?(Я не вижу способа придать Task конкретный поток или указать, чтобы он создавал стек с большим стеком)

Фон (при необходимости): приведенный выше код, который я использовал в течение нескольких месяцев, внезапно начал выбрасыватьисключение переполнения стека.Я считаю, что я только что достиг предела, поскольку теперь он обрабатывает почти 140 тыс. Сущностей со связями, чтобы решить, в каком порядке они должны быть сохранены для инициализации новой базы данных.Я не могу изменить рекурсивную часть - она ​​находится во внешней сторонней библиотеке, которую я использую без планов ее обновления.

Я взломал тестовый код, чтобы доказать, что он действительно работает при обработке большого стеканить.

1 Ответ

0 голосов
/ 12 февраля 2019

Вы можете использовать класс 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();
    }
}
...