Вызвать делегата в конкретном потоке C # - PullRequest
9 голосов
/ 14 августа 2010

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

Допустим, у меня есть:

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

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

[Edit]

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

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

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

Ответы [ 5 ]

3 голосов
/ 14 августа 2010

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

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

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

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

2 голосов
/ 14 августа 2010

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

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

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

1 голос
/ 14 августа 2010

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

Вам нужно будет передать объект 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 );
0 голосов
/ 12 августа 2018

Вот шаблон, который я реализовал для запуска чего-либо в определенном потоке.Преимущество этого состоит в том, что конкретный поток может быть запущен до или после выполнения блокирующих вызовов для работы.Это было для обёртки 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;
0 голосов
/ 14 августа 2010

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

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

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