У меня есть любимый проект, что я работаю над этим, имеет несколько рабочих потоков. Вывод всего к консоли становится трудным следовать, таким образом, я хочу разработать UI, который будет иметь одну область вывода на поток. Я хочу знать, что лучший способ для потоков отправляет обновления UI. У меня есть две идеи:
1) Имейте каждый поток, устанавливает флаг "DataUpdated", когда новые данные доступны, и имейте UI, периодически проверяют на новые данные.
2) Создайте каждый поток с обратным вызовом к Обновлению UI (...) метод, который назовут, когда новые данные станут доступными.
Я в настоящее время склоняюсь (2) по двум причинам: Мне не нравится идея "проверить" каждый поток, и потому что это - мое первое многопоточное приложение, и (2) кажется более простым, чем это, вероятно. Я хочу знать:
Я голосую также за # 2, но с помощью BackgroundWorkers вместо System.Threading.Threads.
-121--4213543-Странно?
(value & 0x1) > 0
Делится ли он на два (четные)?
(value & 0x1) == 0
-121--604484- Можно легко реализовать (2), создав компоненты BackgroundWorker и выполнив работу в их обработчиках DoWork:
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += /* your background work here */;
bw.ProgressChanged += /* your UI update method here */;
bw.RunWorkerAsync();
Каждый BackgroundWorker может сообщать о ходе выполнения в поток пользовательского интерфейса, вызывая EventProgress: хотя это в первую очередь предназначено для отчетности о ходе выполнения ограниченного процесса, это не обязательно - вы также можете передать собственные пользовательские данные, если для этого требуется обновление пользовательского интерфейса. Из обработчика DoWork можно вызвать EventProgress.
Хорошая вещь в BackgroundWorker заключается в том, что он заботится о множестве грязных деталей перекрестной многопоточности для вас. Он также соответствует управляемой событиями модели обновлений, которые вы (правильно) предпочитаете явным обратным вызовам.
В большинстве случаев в большинстве случаев проще всего можно использовать компонент BackgroundWorker, как это предложено в ответе Itowlson, и я бы настоятельно рекомендую использовать этот подход, если это возможно. Если по какой-то причине вы не можете использовать компонент FlowerWorker для вашей цели, например, если вы разрабатываете с .NET 1.1 (Yikes!) Или с компактными рамками, то вам может потребоваться использовать альтернативный подход:
При элементах управления WinForm вы должны избегать модификации элементов управления на любой резьбе, отличной от потока, который изначально создал элемент управления. Компонент Backgroundworker обрабатывает это для вас, но если вы не используете это, то вы можете и должны использовать INVERCEREQUEDIED имущественный метод и включенный в систему System.Windows.Forms.Control Class. Ниже приведен пример, который использует это свойство и способ:
public partial class MultithreadingForm : Form
{
public MultithreadingForm()
{
InitializeComponent();
}
// a simple button event handler that starts a worker thread
private void btnDoWork_Click(object sender, EventArgs e)
{
Thread t = new Thread(WorkerMethod);
t.Start();
}
private void ReportProgress(string message)
{
// check whether or not the current thread is the main UI thread
// if not, InvokeRequired will be true
if (this.InvokeRequired)
{
// create a delegate pointing back to this same function
// the Invoke method will cause the delegate to be invoked on the main UI thread
this.Invoke(new Action<string>(ReportProgress), message);
}
else
{
// txtOutput is a UI control, therefore it must be updated by the main UI thread
if (string.IsNullOrEmpty(this.txtOutput.Text))
this.txtOutput.Text = message;
else
this.txtOutput.Text += "\r\n" + message;
}
}
// a generic method that does work and reports progress
private void WorkerMethod()
{
// step 1
// ...
ReportProgress("Step 1 completed");
// step 2
// ...
ReportProgress("Step 2 completed");
// step 3
// ...
ReportProgress("Step 3 completed");
}
}
Я голосую за № 2, но с фоновыми работами вместо System.threading.threads.
Предпочтительным способом реализации многопоточничества в вашем приложении является использование компонента Projan Worker . Компонент Backgroundworker использует модель, управляемую событиями для многопотативной обработки. Рабочий поток запускает ваш обработчик событий Dowork, а поток, который создает ваши элементы управления, выполняет ваши ProgressChanged и Run WorkerCompleted Deadlers.
Когда вы обновляете свои элементы управления пользовательскими интерфейсами в Progresschanged EventHandler, они автоматически обновляются на главной ните, который помешает вам получить исключения Crossthread.
Посмотрите здесь Например, как использовать Flain Worker.
Если вы создаете свои собственные потоки (неправочные работы или потоки Threadpool), вы можете пройти метод обратного вызова из основного потока, который вызывается из рабочего потока. Это также позволяет передавать аргументы обратным вызовам и даже вернуть значение (например, флаг GO / NO-GO). В вашем обратном вызове вы обновляете пользовательский интерфейс через диспетчеру целевого элемента управления:
public void UpdateUI(object arg)
{
controlToUpdate.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal
, new System.Windows.Threading.DispatcherOperationCallback(delegate
{
controToUpdate.property = arg;
return null;
}), null);
}
}
Вы можете использовать ваши рабочие потоки поднимать события и иметь основную ниту UI добавить обработчики событий. Вам нужно быть осторожным, вы не поднимаете слишком много событий, так как он может стать уродливым, если ваши рабочие потоки поднимают несколько событий в секунду.
Эта статья дает быстрый обзор.