Я пишу плагин для другой программы, которая использует собственную программу для открытия серии файлов для извлечения некоторых данных из. Одной проблемой, которую я имею, является процесс, занимает много времени, и я хочу помешать пользовательскому интерфейсу зависать. Плюс я также хочу дать пользователю способность отменить процесс, прежде чем это завершится. В прошлом я использовал второстепенного рабочего для этого типа вещи, но в этом случае я не думаю, что 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
Я предполагаю, что приложение 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);
}
}
В дополнение к остальным ответам я рекомендую использовать метод обратного вызова из виджета ProcessWidget для передачи прогресса обратно в вызывающий поток. Чтобы преждевременно остановить рабочий поток, можно использовать метод обратного вызова для возврата сигнала остановки в рабочий поток, если он достаточно часто обновляет вызывающего. Или используйте отдельный метод обратного вызова для периодической проверки на предмет go/no-go. Или установите (gasp!) глобальный статический флаг, который периодически проверяет рабочий поток. Или позвоните в Thread.Abort на рабочем потоке и попросите его поймать ThreadAbortException для очистки любых ресурсов.
Как правило, плохая идея имеет несколько потоков в вашем приложении, что каждый создает формы. Невозможно сделать эту работу, но это намного сложнее, чем вы думаете, что это будет потому, что формы, которые находятся в отношениях родительско-дочерних отношений, отправляют сообщения друг другу, и когда они делают, тот, кто отправляет блоки сообщения до одного Получение обрабатывает это.
Смешайте это с прохождением или синхронизацией сообщений между потоками, которые вы выполняете явно, и легко в конечном итоге с тупиками. Итак, в целом вам лучше убедиться, что вы зарезервируете основную нить для вашего пользовательского интерфейса и выполняете всю обработку в других потоках, которые не имеют пользовательского интерфейса.
Если вы соответствуете этому дизайну, то фоновые потоки могут использовать Control.begininvoke , чтобы пройти сообщения в тему пользовательского интерфейса без необходимости ждать обработки сообщений.
Если вы посмотрите на Java Swing, это хороший пример того, как это сделать:
1) Главный поток отвечает за обработку всех запросов UI. Это удаляет все условия гонки из приложения.
2) Каждый раз, когда должна быть выполнена какая-либо «работа», порождайте поток (или пул потоков) и выполняйте работу. Таким образом, основной поток не задерживается, за исключением нескольких микросекунд, а пользовательский интерфейс полностью реагирует на все происходящее.
3) Во всех языках должен быть механизм прерывания потока. В java вы вызываете .interrupt () в потоке, и текущий запущенный поток получает исключение InterruptedException, возникающее везде, где он выполняется. Ваша задача - поймать это исключение, выяснить, действительно ли вы прерваны (прочтите javadocs для этой части), и если вы просто позволили себе умереть (вернитесь из метода run).
1 + 2 = ненавязчивое взаимодействие с клиентом
3 = уничтожение потоков
Альтернативой 3 (если 3 слишком сложный) является предоставление потоку метода .kill (); метод устанавливает флаг уничтожения. Когда вы читаете буфер с жесткого диска в цикле, проверьте, установлен ли флаг уничтожения, если он затем выйдет из цикла, закроет обработчики и вернется из метода запуска.
Изменить: извините, забыл упомянуть отчет о ходе выполнения:
Ваш поток должен иметь общедоступный потокобезопасный метод получения «отчета о ходе выполнения» или, скорее, структуру данных, содержащую информацию о ходе выполнения. Ваш поток пользовательского интерфейса должен периодически (скажем, каждые 0,5 секунды) проверять отчет о ходе выполнения потока и обновлять индикатор выполнения пользовательского интерфейса.И под проверкой потока пользовательского интерфейса я имею в виду, что ваш виджет, который показывает прогресс, отправляет запрос на повторный рендеринг с последней информацией на таймере, пока он не будет выполнен.