В библиотеке классов, которую я пишу, у меня есть метод, позволяющий библиотеке идти и делать некоторые вещи в другом потоке, который делает что-то вроде:

    public void DoStuffAsync(AP p)
    {
        this.Running = true;
        this.Cancel = false;

        ParameterizedThreadStart threadStart = new ParameterizedThreadStart(DoStuff);
        Thread procThread = new Thread(threadStart);
        procThread.Start(p);
    }

У меня также есть ряд событий, объявленных в интерфейсе, к которым может подключиться разработчик, таких как StatusUpdate и ProgressUpdate. В настоящее время я пишу небольшое тестовое приложение (в настоящее время в WPF, хотя я ожидаю такого же поведения в WinForms), которое вызывает DoStuffAsync (), а затем обновляет индикатор выполнения и метку.

К сожалению, на 1-м проходе я получил ошибку, обычный поток не является тем, которому принадлежат элементы управления. Что я хотел бы сделать, так это избавить пользователя от необходимости вызывать Invoke () на стороне пользовательского интерфейса, а также просто подписаться на события и заставить их работать.

Итак, вопрос: есть ли способ сделать это в моем коде при работе с обработчиками событий? В настоящее время запускается так:

        public void UpdateProgress(object sender, ProgressEventArgs e)
        {
            if (handler != null)
            {
                handler(sender, e);
            }
        }
4
Ian 22 Янв 2010 в 16:22

2 ответа

Лучший ответ

Вместо этого используйте AsyncOperationManager.

Он сделает призыв за вас. (внутри он использует SynchronizationContext, как описывает nobugz)

3
adrianm 22 Янв 2010 в 16:54
Это выглядит точно так же, как и ресурс, который мне нужен, на самом деле он входит во всю асинхронную модель и модель, основанную на событиях, что, по сути, и является тем, что я пытаюсь сделать. Хотел бы я знать об этом ресурсе раньше в дизайне!
 – 
Ian
22 Янв 2010 в 17:07
1
Хотя я пошел по этому пути, я фактически вернулся к своему исходному коду. Я обнаружил, что метод SynchronizationContext.Post на самом деле очень медленно перекачивал сообщения обратно в пользовательский интерфейс.
 – 
Ian
19 Фев 2010 в 13:01
Это просто «Я ТОЖЕ», но я бы добавил: в одной выполняемой мной операции, если она была инициирована в потоке / контексте пользовательского интерфейса, она оценивалась бы за доли секунды. При вызове SynchronizationContext.Send (...) из потока, не относящегося к пользовательскому интерфейсу, это заняло несколько минут.
 – 
Yoopergeek
11 Авг 2011 в 02:53

Вам понадобится ссылка на клиентский объект Dispatcher, чтобы вы могли вызвать Dispatcher.Invoke или Dispatcher.BeginInvoke для маршалинга вызова в поток пользовательского интерфейса клиента. Сделайте это, позволив клиенту предоставить вам ссылку, которая вам понадобится, либо через конструктор, либо с помощью свойства.

Другой способ сделать это - сохранить ссылку на SynchronizationContext.Current в конструкторе вашего класса. Используйте его метод отправки или отправки. Однако для этого требуется, чтобы клиент правильно инициализировал WPF (должен быть вызван Application.Run) и сконструировал объект вашего класса из своего потока пользовательского интерфейса.

3
Hans Passant 22 Янв 2010 в 16:45
Круто, а что насчет Winforms? Есть ли там объект «Диспетчер»?
 – 
cmw
22 Янв 2010 в 16:49
- да (.InvokeRequired и Invoke делают то же самое с элементами управления). @nobugz - К сожалению, не уверен, что могу это сделать, в моем примере приложения может быть несколько элементов управления, например метка, индикатор выполнения и холст, которые передают все это в конструкцию и зная, какой из них связан с каким событием, будет громоздко. Я еще не пробовал, но предполагаю, что такие вещи, как WebClient.DownloadAsync (), будут иметь проблемы, аналогичные тем, с которыми я сталкиваюсь, и требуют вызовов?
 – 
Ian
22 Янв 2010 в 16:53
Не проблема, есть только один объект Dispatcher для всех элементов управления.
 – 
Hans Passant
22 Янв 2010 в 16:57
Интересно, я мало знаю о диспетчере.
 – 
Ian
22 Янв 2010 в 17:06