Вызов делегата в определенном потоке C#

есть ли способ заставить делегата работать в определенном потоке?

скажем, у меня есть:

CustomDelegate del = someObject.someFunction;
Thread dedicatedThread = ThreadList[x];

могу ли я иметь последовательный фоновый длинный поток и вызывать своих делегатов на нем, когда мне это нужно. Каждый раз одна и та же нить.

[Edit]

причина, по которой я хочу, чтобы он был в выделенном потоке, заключается в том, что я намерен запустить делегат на нем и приостановить поток после y миллисекунды, и возобновите поток, когда я запускаю другого делегата на нем. Я вижу, что это невозможно. У меня будет очередь делегатов, и пусть основная функция потока читается и запускается из нее.

чтобы уточнить конкретный пример, у меня есть игровая система с кучей потоков игроков. Я хотел бы, чтобы каждый playerthread запускал обработчики событий для игровых событий на нем. Если обработчики событий занимают слишком много времени, я хотел бы иметь возможность приостановить этот конкретный игрок до следующего события, приостановив его поток.

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

5 ответов


Я думаю, что лучшее решение-использовать Task объекты и очередь их в StaThreadScheduler запуск одного потока.

кроме того, вы можете использовать ActionThread на Нито.Async чтобы создать обычный поток со встроенной очередью Action делегатов.

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

вся эта сложность очень близка к системе планирования потоков, поэтому я рекомендую сделать шаг назад и сделать больше перепроектирования. Вы можете позволить каждому действию быть в очереди к ThreadPool (Я рекомендую просто иметь каждый из них быть


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

простой подход, чтобы создать очереди обработки событий на выделенном потоке, как LBushkin упоминает. Я предлагаю использовать Queue<Action> класс и вызов делегата действия напрямую. Большинство задач можно выполнить с помощью анонимных действий делегата.

наконец, как предупреждение, я бы предложил вам использовать семафор или EventWaitHandle вместо Thread.Спите на выделенной нити. Это определенно более дружелюбно, чем выполнение вашего фонового цикла снова и снова, когда это не нужно.


обычно я бы предложил просто использовать пул потоков или BackgroundWorker class-но это не гарантирует, что работа будет происходить в любом конкретном потоке. Непонятно, почему вас волнует, какой поток запускает работы, но предполагая, что это имеет какое-то значение ...

вам придется пройти Delegate объект через какую-то общую память, например, очередь. Фоновый поток должен был бы наблюдать за этой очередью, вытаскивать делегатов из нее, когда они существуют, и выполнять их.

если окажется, что пул потоков допустим для запуска вашего кода, Вы всегда можете использовать BeginInvoke метод делегата для этого:

// wrap your custom delegate in an action for simplicity ...
Action someCode = () => yourCustomDelegate( p1, p2, p3, ... );
// asynchronously execute the currying Action delegate on the threadpool...
someCode.BeginInvoke( someCode.EndInvoke, action );

для создаваемых потоков можно указать только делегат ThreadStart при их создании. Нет никакого положения для инъекции другого делегата в созданный поток. Пул потоков отличается тем, что позволяет отправлять делегаты в ранее созданные потоки, которые он запускает от вашего имени.

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


вот шаблон, который я реализовал, чтобы запустить что-то в определенном потоке. Преимущество этого заключается в том, что конкретный поток может быть запущен до или после блокировки вызовов для работы. Это было для оболочки COM-объекта под названием Watin для управления экземплярами Internet Explorer, который чрезвычайно чувствителен к контексту. Он может быть расширен, чтобы удалить Thread.Sleep(), возможно,AutoResetEvent, что значительно улучшило бы производительность в определенных ситуациях.

идеи для эта схема была взята из ответов Джастина Брайтфеллера, Стивена Клири и Лбушкина.

    private Instantiate()
    {
        BrowserQueue = new ConcurrentQueue<BrowserAction>();
        BrowserThread = new Thread(new ThreadStart(BrowserThreadLoop));
        BrowserThread.SetApartmentState(ApartmentState.STA);
        BrowserThread.Name = "BrowserThread";
        BrowserThread.Start();
    }
    private static void BrowserThreadLoop()
    {
        while (true)
        {
            Thread.Sleep(500);
            BrowserAction act = null;
            while (Instance.BrowserQueue.TryDequeue(out act))
            {
                try
                {
                    act.Action();
                }
                catch (Exception ex) { }
                finally
                {
                    act.CompletionToken.Set();
                }
            }
        }
    }
    public static void RunBrowserAction(Action act)
    {
        BrowserAction ba = new BrowserAction() { Action = act, CompletionToken = new ManualResetEvent(false) };
        Instance.BrowserQueue.Enqueue(ba);
        ba.CompletionToken.WaitOne();
    }
    public class BrowserAction
    {
        public Action Action { get; set; } = null;
        public ManualResetEvent CompletionToken { get; set; } = null;
    }
    ConcurrentQueue<BrowserAction> BrowserQueue;