#c# #multithreading #backgroundworker
#c# #многопоточность #backgroundworker
Вопрос:
Использование C # .NET 4.0
Я пытаюсь использовать BackgroundWorker для получения списка служб на удаленном компьютере. Это выполняется в фоновом режиме, поэтому пользовательский интерфейс остается отзывчивым во время ожидания информации.
Мой подход заключается в том, чтобы поместить запрос на информацию в метод _DoWork фонового рабочего и использовать ManualResetEvent.Set(), чтобы указать, что информация была обработана. Значение ManaualResetEvent должно быть установлено в методе _RunWorkerCompleted. _RunWorkerCompleted никогда не выполняется.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.ServiceProcess;
using System.Threading;
using System.Diagnostics;
namespace DemoTest
{
public partial class Form1 : Form
{
List<ServiceController> MyBox1Services;
ManualResetEvent MyBox1ServicesObtained = new ManualResetEvent(false);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Use background worker to get the list of services in the background
BackgroundWorker MyBox1Servicess = new BackgroundWorker();
MyBox1Servicess.DoWork = new DoWorkEventHandler(bwGetMyBox1Services_DoWork);
MyBox1Servicess.RunWorkerCompleted = new RunWorkerCompletedEventHandler(MyBox1Servicess_RunWorkerCompleted);
MyBox1Servicess.RunWorkerAsync();
// Wait until all the services have been obtained before moving on
MyBox1ServicesObtained.Reset();
MyBox1ServicesObtained.WaitOne();
// Update the GUI Here....
}
void MyBox1Servicess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
MessageBox.Show("Canceled!");
}
else if (!(e.Error == null))
{
MessageBox.Show(e.Error.Message);
}
else
{
Debug.WriteLine("MyBox1Servicess_RunWorkerCompleted");
}
MyBox1Services = (List<ServiceController>)e.Resu<
MyBox1ServicesObtained.Set();
}
void bwGetMyBox1Services_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
ServiceController[] allServices;
List<ServiceController> allServicesList = new List<ServiceController>();
try
{
allServices = ServiceController.GetServices("MyBox1");
foreach (ServiceController sc in allServices)
{
allServicesList.Add(sc);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
e.Result = allServicesList;
}
}
}
Ответ №1:
Это потому, что RunWorkerCompleted
событие запускается в потоке пользовательского интерфейса, который вы заблокировали в обработчике нажатия кнопки с ManualResetEvent
вызовом WaitOne
.’
Вместо того, чтобы использовать ManualResetEvent
, почему бы просто не обновить ваш графический интерфейс в RunWorkerCompleted
обработчике событий?
Комментарии:
1. Да, обработчик нажатия кнопки должен завершиться как можно скорее
2. Спасибо. Фактическое приложение извлекает службы с 10 разных серверов, поэтому я собирался дождаться их завершения, прежде чем двигаться дальше. (10 MannualResetEvents). Я найду альтернативный метод сигнализации. Вероятно, мне следует использовать потоки вместо фонового рабочего (?)
3. @darwin — Bgw инкапсулирует поток. Но вам следует избегать блокирования вашего основного потока (GUI).
4. Кроме того, если вы хотите распараллелить свою работу, я бы заглянул в библиотеку параллельных задач, которая позаботится о большей части этой синхронизации для вас.
5. 1 за выделение ошибки ожидания в обработчике событий пользовательского интерфейса, когда потоки что-то завершают.