Вызов делегата в определенном потоке 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;