Запустить событие в исходном потоке внутри AsyncCallback (консольное приложение) - PullRequest
1 голос
/ 18 января 2012

Я использую Func <> или Action <>. BeginInvoke для асинхронного запуска методов с помощью ThreadPool.

Возможно ли, чтобы AsyncCallback вызывал функцию (или событие, технически) в исходном потоке, который породил новый поток?

Я знаю, что в приложениях WinForms вы можете использовать Control / ISynchronizeInvoke / Dispatcher для оповещения потока пользовательского интерфейса о завершении асинхронных операций, но это не приложение WinForms и, похоже, оно не работает.

    class SyncTest : System.ComponentModel.ISynchronizeInvoke
    {
        public void TestMethod() {
            Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId + " Test Method Fired.");
        }

        public IAsyncResult BeginInvoke(Delegate method, object[] args) {
            throw new NotImplementedException();
        }

        public object EndInvoke(IAsyncResult result) {
            throw new NotImplementedException();
        }

        public object Invoke(Delegate method, object[] args) {
            return method.DynamicInvoke(args); 
        }

        public bool InvokeRequired {
            get { throw new NotImplementedException(); }
        }

        public void Test()
        {
            var sleep = new Action(() => System.Threading.Thread.Sleep(5000));

            System.ComponentModel.ISynchronizeInvoke originalThreadCallback = (System.ComponentModel.ISynchronizeInvoke)this;

            for (int i = 0; i < 5; i++)
            {
                sleep.BeginInvoke(new AsyncCallback(res =>
                {
                    (res.AsyncState as Action).EndInvoke(res);
                    Console.WriteLine("Thread inside callback: " + System.Threading.Thread.CurrentThread.ManagedThreadId); 
                    originalThreadCallback.Invoke(new Action(() => this.TestMethod()), null);
                }), sleep);
            }
        }
    }

Выходные данные метода Test () следующие:

Основная нить = 9

ТЕСТ СДЕЛАНО

Тема внутри обратного вызова: 11

Тема внутри обратного вызова: 10

10 Метод испытаний запущен.

11 Метод испытания запущен.

Тема внутри обратного вызова: 12

12 Метод испытания запущен.

Тема внутри обратного вызова: 13

13 Метод испытания сработал.

Тема внутри обратного вызова: 14

14 Метод испытания сработал.

Как вы видите, в исходном потоке ничего не вызывается с ID = 9.

Очевидно, что моя реализация ISynchronizeInvoke на самом деле ничего не делает для вызова метода Test в исходном потоке, и в этом проблема - но я также не могу получить экземпляр ISynchronizeInvoke из любого из моих делегатов или событий (это всегда ноль). Какие объекты в .NET 4 правильно реализуют интерфейс?

Спасибо.

1 Ответ

2 голосов
/ 18 января 2012

Что ж, у вас должен быть какой-то цикл, как в Windows Forms (и т. Д.), Ожидающий обработки для обработки.

Вы можете написать это самостоятельно, но это будет означать, что ваш оригинальный поток должен быть относительно свободным, точно так же, как вы не хотите блокировать поток пользовательского интерфейса в приложении Windows Forms. По сути, вы бы переписали часть инфраструктуры, которую использует WinForms. Я ничего не знаю о том, что делает это из коробки, хотя классы очереди производителя / потребителя .NET 4 (например, BlockingCollection<T>) как минимум помогут .

Документы для BlockingCollection<T> дают быстрый пример того, как реализовать очередь производителя / потребителя - в основном ваши потоки потоков будут вызывать Add в очереди, а ваш потребительский поток будет вызывать Take для нее (или TryTake).

Также есть видео Channel 9 * с экспертом Стивеном Таубом (из параллельной команды в MS) об использовании BlockingCollection.

...