Есть ли способ использовать Dispatcher в потоке без WPF; Новое в многопоточности - PullRequest
3 голосов
/ 31 августа 2010

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

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

Мои другие возможные варианты: Как я понимаю, возможный способ сделать это - реализовать очередь. В который я мог бы вставить методы, которые я хочу запустить в определенном потоке. В этом конкретном потоке я буду вращаться и спать / 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.
    }
}

1 Ответ

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

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

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

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

...