Как я связываюсь между несколькими потоками?

Я пишу плагин для другой программы, которая использует собственную программу для открытия серии файлов для извлечения некоторых данных из. Одной проблемой, которую я имею, является процесс, занимает много времени, и я хочу помешать пользовательскому интерфейсу зависать. Плюс я также хочу дать пользователю способность отменить процесс, прежде чем это завершится. В прошлом я использовал второстепенного рабочего для этого типа вещи, но в этом случае я не думаю, что BackgroundWorker будет работать.

Для создания плагина через API, который я использую, можно создать пользовательскую команду путем наследования интерфейсу IAPICommand. Этот интерфейс включает Выполнение (Приложение приложения) метод. Класс затем инстанцируют и Выполнение (), метод называет программа, когда пользователь вызывает пользовательскую команду в программе.

Выполнение () метод передается ссылка на текущий Объект приложения, когда это называют, и именно этот объект приложения используется для открытия файлов для извлечения данных из. Однако экземпляр приложения не может открыть документ по требованию потока другой, оригинал Выполняет () поток.

Таким образом, обычно UI существовал бы на основном потоке, и трудоемкое извлечение данных будет выполнено на вторичном потоке. Однако в этом случае извлечение данных должно быть выполнено на основном потоке, и я должен создать вторичный поток для UI.

Вот является разделенный вниз версией кода.

class MyCommand:IAPICommand
{
    public void Execute(Application app) // method from IAPICommand
    {
        Thread threadTwo= new Thread(ShowFormMethod);
        threadTwo.Start();
    }

    public void ProcessWidget(Widget w, Application app)
    { 
        //uses an App to work some magic on C
        //app must be called from the original thread that called ExecuteCommand()
    }

    //method to open custom form on a seperatethread
    public void ShowFormMethod()
    {
      MyForm form = new MyForm();
      form.ShowDialog();  
    }
}

Вот блок-схема, которая показывает, как я думаю, что это должно в конечном счете работать.

сопроводительный текст http://dl.dropbox.com/u/113068/SOMLibThreadingDiagram.jpg

  1. Эта схема имеет какой-либо смысл, и раз так я даже проявляю корректный подход для решения этой проблемы?
  2. После того как основной поток запускает поток UI, я хочу, чтобы он ожидал пользователя для или выбора виджетов, чтобы обработать, или закончить команду путем закрытия формы (красные числа по схеме). Как я могу заставить основной поток ожидать, и как я инициировал его, чтобы продолжиться или обработкой или продолжиться в конец, когда поток UI заканчивается? Я думал, что у меня мог быть основной поток, ожидают на блокировке Монитора. Поток UI затем заполнил бы статический список Виджетов, которые будут обработаны и затем пульсировал бы основной поток для инициирования обработки. Поток UI также пульсировал бы Основной поток, когда форма закрывается, и основной поток знал бы для продолжения в конец команды, если бы это когда-либо пульсировалось, когда список виджетов для обработки был пуст.
  3. Как я позволяю основному потоку передавать прогресс или завершение обработки виджета назад к потоку UI (желтые стрелки в схеме)? Я просто использовал BeginInvoke () метод Формы, чтобы сделать это?
  4. Как я позволяю потоку UI отменять обработку виджета (зеленая стрелка в схеме)? Я думаю, что мог просто установить статический булев флаг, который проверяется, прежде чем каждый виджет обрабатывается?

11
задан Eric Anastas 2 February 2010 в 02:10
поделиться

4 ответа

Я предполагаю, что приложение Host представляет собой приложение WinForms Отказ

Вам необходимо сохранить SynchronizationContext из исходной резьбы в вашем методе , затем вызовите его Отправить метод выполнения кода в потоке UI хоста.

Например:

class MyCommand:IAPICommand
{
    SynchronzationContext hostContext;
    public void Execute(Application app) // method from IAPICommand
    {
        hostContext = SynchronzationContext.Current;
        Thread threadTwo = new Thread(ShowFormMethod);
        threadTwo.Start();
    }

    public void ProcessWidget(Widget w, Application app)
    { 
        //uses an App to work some magic on C
        //app must be called from the original thread that called ExecuteCommand()
        SomeType someData = null;
        hostContext.Send(delegate { someData = app.SomeMethod(); }, null);
    }
}
1
ответ дан 3 December 2019 в 07:12
поделиться

В дополнение к остальным ответам я рекомендую использовать метод обратного вызова из виджета ProcessWidget для передачи прогресса обратно в вызывающий поток. Чтобы преждевременно остановить рабочий поток, можно использовать метод обратного вызова для возврата сигнала остановки в рабочий поток, если он достаточно часто обновляет вызывающего. Или используйте отдельный метод обратного вызова для периодической проверки на предмет go/no-go. Или установите (gasp!) глобальный статический флаг, который периодически проверяет рабочий поток. Или позвоните в Thread.Abort на рабочем потоке и попросите его поймать ThreadAbortException для очистки любых ресурсов.

2
ответ дан 3 December 2019 в 07:12
поделиться

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

Смешайте это с прохождением или синхронизацией сообщений между потоками, которые вы выполняете явно, и легко в конечном итоге с тупиками. Итак, в целом вам лучше убедиться, что вы зарезервируете основную нить для вашего пользовательского интерфейса и выполняете всю обработку в других потоках, которые не имеют пользовательского интерфейса.

Если вы соответствуете этому дизайну, то фоновые потоки могут использовать Control.begininvoke , чтобы пройти сообщения в тему пользовательского интерфейса без необходимости ждать обработки сообщений.

14
ответ дан 3 December 2019 в 07:12
поделиться

Если вы посмотрите на Java Swing, это хороший пример того, как это сделать:

1) Главный поток отвечает за обработку всех запросов UI. Это удаляет все условия гонки из приложения.

2) Каждый раз, когда должна быть выполнена какая-либо «работа», порождайте поток (или пул потоков) и выполняйте работу. Таким образом, основной поток не задерживается, за исключением нескольких микросекунд, а пользовательский интерфейс полностью реагирует на все происходящее.

3) Во всех языках должен быть механизм прерывания потока. В java вы вызываете .interrupt () в потоке, и текущий запущенный поток получает исключение InterruptedException, возникающее везде, где он выполняется. Ваша задача - поймать это исключение, выяснить, действительно ли вы прерваны (прочтите javadocs для этой части), и если вы просто позволили себе умереть (вернитесь из метода run).

1 + 2 = ненавязчивое взаимодействие с клиентом

3 = уничтожение потоков

Альтернативой 3 (если 3 слишком сложный) является предоставление потоку метода .kill (); метод устанавливает флаг уничтожения. Когда вы читаете буфер с жесткого диска в цикле, проверьте, установлен ли флаг уничтожения, если он затем выйдет из цикла, закроет обработчики и вернется из метода запуска.

Изменить: извините, забыл упомянуть отчет о ходе выполнения:

Ваш поток должен иметь общедоступный потокобезопасный метод получения «отчета о ходе выполнения» или, скорее, структуру данных, содержащую информацию о ходе выполнения. Ваш поток пользовательского интерфейса должен периодически (скажем, каждые 0,5 секунды) проверять отчет о ходе выполнения потока и обновлять индикатор выполнения пользовательского интерфейса.И под проверкой потока пользовательского интерфейса я имею в виду, что ваш виджет, который показывает прогресс, отправляет запрос на повторный рендеринг с последней информацией на таймере, пока он не будет выполнен.

0
ответ дан 3 December 2019 в 07:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: