Предложения для легкого, поточно-ориентированного планировщика - PullRequest
1 голос
/ 17 января 2011

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

Моя текущая идея состоит в том, чтобы иметь круговой двусвязный список, в котором каждое волокно является узлом, а планировщик содержит ссылку на текущий узел.Это то, что я имею до сих пор:

using Interlocked = System.Threading.Interlocked;

public class Thread {
    internal Future current_fiber;

    public void RunLoop () {
        while (true) {
            var fiber = current_fiber;
            if (fiber == null) { 
                // block the thread until a fiber is scheduled
                continue;
            }
            if (fiber.Fulfilled)
                fiber.Unschedule ();
            else
                fiber.Resume ();

            //if (current_fiber == fiber) current_fiber = fiber.next;
            Interlocked.CompareExchange<Future> (ref current_fiber, fiber.next, fiber);      
        }
    }
}

public abstract class Future {
    public bool Fulfilled { get; protected set; }
    internal Future previous, next;

    // this must be thread-safe
    // it inserts this node before thread.current_fiber
    // (getting the exact position doesn't matter, as long as the
    //  chosen nodes haven't been unscheduled)
    public void Schedule (Thread thread) {
        next = this; // maintain circularity, even if this is the only node
        previous = this;
    try_again:
        var current = Interlocked.CompareExchange<Future> (ref thread.current_fiber, this, null);
        if (current == null)
            return;

        var target = current.previous;
        while (target == null) {
            // current was unscheduled; negotiate for new current_fiber
            var potential = current.next;
            var actual = Interlocked.CompareExchange<Future> (ref thread.current_fiber, potential, current);
            current = (actual == current? potential : actual);
            if (current == null)
                goto try_again;
            target = current.previous;
        }
        // I would lock "current" and "target" at this point.
        // How can I do this w/o risk of deadlock?
        next = current;
        previous = target;
        target.next = this;
        current.previous = this;
    }

    // this would ideally be thread-safe
    public void Unschedule () {
        var prev = previous;
        if (prev == null) {
            // already unscheduled
            return;
        }
        previous = null;
        if (next == this) {
            next = null;
            return;
        }
        // Again, I would lock "prev" and "next" here
        // How can I do this w/o risk of deadlock?            
        prev.next = next;
        next.previous = prev;
    }

    public abstract void Resume ();
}

Как вы можете видеть, моя проблема в том, что я не могу обеспечить порядок блокировки, поэтому я не могу заблокировать более одного узла без риска тупиковой ситуации.Или я могу?Я не хочу иметь глобальную блокировку для объекта Thread, поскольку количество конфликтов блокировок будет чрезмерным.Кроме того, меня не особо волнует положение вставки, поэтому, если я блокирую каждый узел отдельно, тогда Schedule () может использовать что-то вроде Monitor.TryEnter и просто продолжать идти по списку, пока не найдет разблокированный узел.Я не вкладываюсь ни в какую конкретную реализацию, если она соответствует требованиям, которые я упомянул.Любые идеи очень приветствуются.Спасибо!

РЕДАКТИРОВАТЬ - Комментарии показывают, что люди думают, что я говорю о волокнах winapi (а я нет).Короче говоря, все, что я хочу сделать, - это запланировать выполнение битов кода один за другим в потоке single .Он похож на TTP / Async CTP, но AFIK не гарантирует продолжения в том же потоке, если это не поток UI.Я открыт для альтернативных предложений о том, как реализовать вышеизложенное, но, пожалуйста, не говорите «не используйте волокна».

Ответы [ 4 ]

2 голосов
/ 18 января 2011

Используйте библиотеку параллельных задач.

Создайте пользовательский TaskScheduler, как показано в MSDN.В вашем собственном планировщике задач, если вам нужен только один поток, вы можете иметь только один поток.

Если вы хотите предотвратить встроенное планирование задач, переопределите protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) и верните false.

0 голосов
/ 07 июля 2011

Вы можете использовать простую очередь сообщений без блокировки , чтобы ставить в очередь свои фьючерсы.

Вместо того, чтобы сохранять циклическую структуру данных, вы вытаскиваете Future из очереди перед вызовом Resume () и добавляете его по завершении (если есть еще работа).

0 голосов
/ 18 января 2011

Я предполагаю, что запуск в одном потоке не требуется, только одна задача за раз выполняется до завершения. В этом случае загрузите образцы TPL . Рассмотрите возможность использования LimitedConcurrencyLevelTaskScheduler с максимальным значением параллелизма 1.

0 голосов
/ 17 января 2011

Не используйте Fibers с .NET, прочитайте что-нибудь от Джо Даффи на эту тему. Вместо этого используйте TPL для правильной реализации планировщика пользовательского режима.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...