Есть ли способ использовать Dispatcher в потоке, отличном от WPF; новичок в многопоточности

Почему это не работает?

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

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

Моя цель: избежать всей тяжелой работы по созданию очереди делегатов, поддержанию блокировки и т. Д. Похоже, что в мире WPF существует готовое решение под названием Dispatcher. Элементы управления WPF в основном наследуются от DispatcherObject, и каким-то образом все это работает. Что мне нужно сделать, чтобы получить эту работу?

using System;
using System.Threading;
using System.Windows.Threading;

namespace ThreadingTrials
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "mainThread";
            Engine engine = new Engine();
            Console.WriteLine("initializing SpecialEngine from {0}", Thread.CurrentThread.Name);
            engine.initialize();
            engine.doWork();
        }
    }
    class Engine:DispatcherObject
    {
        private EventWaitHandle InitializationComplete;
        private EventWaitHandle newWorkComplete;
        //private Dispatcher dispatcher;
        public Engine()
        {

        }
        public void initialize()
        {
            InitializationComplete = new EventWaitHandle(false, EventResetMode.ManualReset);
            Thread thread = new Thread(new ParameterizedThreadStart((hwnd)=>
            {
                InitializeSpecialEngineObject();
                while (true) ;
            }));
            thread.Name = "Special Engine Thread";

            thread.SetApartmentState(ApartmentState.STA);
            thread.Priority = ThreadPriority.Normal;
            thread.Start();
            Console.WriteLine("waiting for initialize at {0}", Thread.CurrentThread.Name);
            InitializationComplete.WaitOne();
        }
        private void InitializeSpecialEngineObject()
        {
            Console.WriteLine("doing initialization at {0}", Thread.CurrentThread.Name);
            Thread.Sleep(500);
            //dispatcher = Dispatcher.CurrentDispatcher;
            InitializationComplete.Set();
        }

        internal void doWork()
        {
            newWorkComplete = new EventWaitHandle(false, EventResetMode.AutoReset);
            //Dispatcher.Thread.Suspend();
            Dispatcher.Invoke((SendOrPostCallback)delegate
                {
                    Console.WriteLine("dispatched to {0}", Thread.CurrentThread.Name);
                    Thread.Sleep(500);
                    newWorkComplete.Set();
                },DispatcherPriority.Background, null);
            Dispatcher.Thread.Resume();
            Console.WriteLine("waiting for new work to complete at {0}", Thread.CurrentThread.Name);
            newWorkComplete.WaitOne();
        }
        private void doingWork()
        {
            Console.WriteLine("Doing work in {0}", Thread.CurrentThread.Name);

            Thread.Sleep(500);
        }
    }
}

Спасибо за вклад. Справедливо. На самом деле было очень мало работы, чтобы создать простой рабочий поток, который ожидает события, обозначающего новую задачу в очереди делегатов void (), и запускает их по мере того, как они становятся доступными. Я скопировал большую часть кода с веб-сайта в Интернете ... К сожалению, ссылка утеряна. Я сделал это в тот день и должен был отредактировать этот пост раньше.

using System;
using System.Threading;
using System.Collections.Generic;

class ProducerConsumerQueue : IDisposable
{
    EventWaitHandle _wh = new AutoResetEvent(false);
    Thread _worker;
    readonly object _locker = new object();
    Queue<Action> _tasks = new Queue<Action>();

    public delegate void Action();

    public ProducerConsumerQueue()
    {
        _worker = new Thread(Work);
        _worker.Start();
    }

    public void EnqueueTask(Action work)
    {
        lock (_locker) _tasks.Enqueue(work);
        _wh.Set();
    }

    public void Dispose()
    {
        EnqueueTask(null);     // Signal the consumer to exit.
        _worker.Join();         // Wait for the consumer's thread to finish.
        _wh.Close();            // Release any OS resources.
    }

    void Work()
    {
        while (true)
        {
            Action task = null;
            lock (_locker)
                if (_tasks.Count > 0)
                {
                    task = _tasks.Dequeue();
                    if (task == null) return;
                }
            if (task != null)
            {
                task.Invoke();
            }
            else
                _wh.WaitOne();         // No more tasks - wait for a signal
        }
    }

}
class Program
{
    static void Main()
    {
        using (ProducerConsumerQueue q = new ProducerConsumerQueue())
        {
            q.EnqueueTask(delegate 
            {
                Console.WriteLine("Performing task: Hello");
                Thread.Sleep(1000);  // simulate work...
            });
            for (int i = 0; i < 10; i++) q.EnqueueTask(delegate 
            {
                Console.WriteLine("Performing task: "+ i);
                Thread.Sleep(1000);  // simulate work...
            });
            q.EnqueueTask(delegate 
            {
                Console.WriteLine("Performing task: Goodbye!");
                Thread.Sleep(1000);  // simulate work...
            });
        }

        // Exiting the using statement calls q's Dispose method, which
        // enqueues a null task and waits until the consumer finishes.
    }
}

person schak    schedule 30.08.2010    source источник


Ответы (1)


Вы не звоните Dispatcher.Run. Вы предполагаете, что Dispatcher содержит Thread, который выполняет свою работу, но это наоборот. При первом вызове Dispatcher.CurrentDispatcher создается Dispatcher, привязанный к текущему потоку. Обратите внимание, что даже если ваш код не вызывает CurrentDispatcher напрямую, он делает это косвенно, создавая DispatcherObject (который захватывает CurrentDispatcher в поле).

Просмотрите документы по модели потоковой передачи WPF, которые содержат все необходимые сведения. .

Если вам нужна диспетчерская функциональность в дочернем потоке, но вы не хотите зависеть от WPF, вы можете использовать класс ActionThread из Nito.Async, что примерно эквивалентно Dispatcher плюс выделенный Thread.

person Stephen Cleary    schedule 30.08.2010
comment
Повторяя предупреждение, которое я сделал в другом месте, исходный дизайн API Microsoft здесь приводит к ошибочным ожиданиям; в то время как Dispatcher.​CurrentDispatcher является свойством с побочными эффектами (достаточно плохо!), у нас также есть Dispatcher.FromThread(…), метод , который делает то же самое… за исключением того, что теперь без побочных эффектов. Остерегаться! - person Glenn Slayden; 12.02.2021